home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 2001-11-19 | 970.6 KB | 28,679 lines
/* ** Copyright Microsoft, Inc. 1998-2000 ** All Rights Reserved. */ set nocount on go execute dbo.sp_configure 'update',1 go reconfigure with override go set ANSI_NULLS off go use master go dump tran master with no_log go /* ** Drop the stored procedures in this script using the old dropping SP ** and then drop itself */ if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_rlcore') begin drop procedure sp_MSdrop_rlcore end /* ** Create stored procedures to drop the stored procedures ** created by this script */ raiserror('Creating procedure sp_MSdrop_rlcore', 0,1) GO create procedure sp_MSdrop_rlcore as if exists (select * from sysobjects where type = 'P' and name = 'sp_MSguidtostr') drop procedure sp_MSguidtostr if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpdestowner') drop procedure sp_MShelpdestowner if exists (select * from sysobjects where type = 'P' and name = 'sp_MSchangeobjectowner') drop procedure sp_MSchangeobjectowner if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetcolumnlist') drop procedure sp_MSgetcolumnlist if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetconflicttablename') drop procedure sp_MSgetconflicttablename if exists (select * from sysobjects where type = 'P' and name = 'sp_MSuniquetempname') drop procedure sp_MSuniquetempname if exists (select * from sysobjects where type = 'P' and name = 'sp_MSuniqueobjectname') drop procedure sp_MSuniqueobjectname if exists (select * from sysobjects where type = 'P' and name = 'sp_MSfillupmissingcols') drop procedure sp_MSfillupmissingcols if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddguidcolumn') drop procedure sp_MSaddguidcolumn if exists (select * from sysobjects where type = 'P' and name = 'sp_MSprepare_mergearticle') drop procedure sp_MSprepare_mergearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSuniquecolname') drop procedure sp_MSuniquecolname if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddguidindex') drop procedure sp_MSaddguidindex if exists (select * from sysobjects where type = 'P' and name = 'sp_MSrefcnt') drop procedure sp_MSrefcnt if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgentablenickname') drop procedure sp_MSgentablenickname if exists (select * from sysobjects where type = 'P' and name = 'sp_MStablenickname') drop procedure sp_MStablenickname if exists (select * from sysobjects where type = 'P' and name = 'sp_MStablenamefromnick') drop procedure sp_MStablenamefromnick if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetmakegenerationapplock') drop procedure sp_MSgetmakegenerationapplock if exists (select * from sysobjects where type = 'P' and name = 'sp_MSreleasemakegenerationapplock') drop procedure sp_MSreleasemakegenerationapplock if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakegeneration') drop procedure sp_MSmakegeneration if exists (select * from sysobjects where type = 'P' and name = 'sp_MSfixlineageversions') drop procedure sp_MSfixlineageversions if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddupdatetrigger') drop procedure sp_MSaddupdatetrigger if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddmergetriggers') drop procedure sp_MSaddmergetriggers if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmaptype') drop procedure sp_MSmaptype if exists (select * from sysobjects where type = 'P' and name = 'sp_MSquerysubtype') drop procedure sp_MSquerysubtype if exists( select * from sysobjects where type = 'P ' and name = 'sp_showrowreplicainfo') begin drop procedure sp_showrowreplicainfo end if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSsethighestversion') begin drop procedure sp_MSsethighestversion end if exists( select * from sysobjects where type = 'P ' and name = 'sp_mergemetadataretentioncleanup') begin drop procedure sp_mergemetadataretentioncleanup end if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSpurgecontentsorphans') begin drop procedure sp_MSpurgecontentsorphans end if exists( select * from sysobjects where type = 'P ' and name = 'sp_MScleanup_zeroartnick_genhistory') begin drop procedure sp_MScleanup_zeroartnick_genhistory end if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSdelete_specifiedcontents') begin drop procedure sp_MSdelete_specifiedcontents end go exec dbo.sp_MS_marksystemobject sp_MSdrop_rlcore go exec dbo.sp_MSdrop_rlcore go dump tran master with no_log go raiserror('Creating procedure sp_MSguidtostr', 0, 1) GO create proc sp_MSguidtostr (@guid uniqueidentifier, @mystr nvarchar(32) output) as declare @guidstr nvarchar(36) set @guidstr = convert(nchar(36), @guid) set @mystr = substring(@guidstr, 1, 8) + substring(@guidstr, 10, 4) + substring(@guidstr, 15, 4)+ substring(@guidstr, 20, 4)+ substring(@guidstr, 25, 12) go exec dbo.sp_MS_marksystemobject sp_MSguidtostr go raiserror('Creating procedure sp_MSgetconflicttablename', 0,1) GO CREATE PROCEDURE sp_MSgetconflicttablename @publication sysname, @source_object nvarchar(520), @conflict_table sysname = NULL OUTPUT AS declare @objid int declare @retcode int declare @current_conflict sysname declare @object_name sysname declare @name_out sysname declare @pubid uniqueidentifier declare @article sysname declare @tablename sysname declare @ownername sysname select @tablename = null select @ownername = null select @pubid=pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name() select @objid = object_id(@source_object) -- Raise error if objid is still null at this point if @objid is null begin declare @db_name sysname select @db_name = db_name() raiserror(15009,16, -1,@source_object,@db_name) return 1 end select @article=name, @current_conflict = conflict_table from sysmergearticles where objid = @objid and pubid=@pubid if @current_conflict is not NULL begin if @conflict_table is NULL select @current_conflict else select @conflict_table = @current_conflict return (0) end if len(@publication) + len(@article) > 118 -- SYSNAME minus 'conflict_' select @object_name = 'conflict_' + convert(nvarchar(59), @publication) + '_' + convert(nvarchar(59), @article) else select @object_name = 'conflict_' + @publication + '_' + @article exec @retcode = dbo.sp_MSuniqueobjectname @object_name, @conflict_table OUTPUT GO exec dbo.sp_MS_marksystemobject sp_MSgetconflicttablename go grant exec on dbo.sp_MSgetconflicttablename to public go raiserror('Creating procedure sp_MSuniqueobjectname', 0,1) GO CREATE PROCEDURE sp_MSuniqueobjectname @name_in sysname, @name_out sysname = NULL output AS declare @name_out_local sysname declare @subschars sysname declare @curchar nchar(1) declare @substidx int declare @pos int select @subschars = 'abcdefghijklmnopqrstuvwxyz' select @name_out_local = @name_in select @substidx = 0 select @pos = 1 while exists (select * from sysobjects where name = @name_out_local) begin if @substidx > 25 begin select @pos = @pos + 1 select @substidx = 1 end else select @substidx = @substidx + 1 select @curchar = substring(@subschars, @substidx, 1) select @name_out_local = stuff(@name_out_local, @pos, 1, @curchar) end if @name_out IS NULL select @name_out_local else select @name_out = @name_out_local return (0) GO exec dbo.sp_MS_marksystemobject sp_MSuniqueobjectname go raiserror('Creating procedure sp_MSuniquetempname', 0,1) GO CREATE PROCEDURE sp_MSuniquetempname @name_in sysname, @name_out sysname output AS declare @subschars nvarchar(26) declare @curchar nchar(1) declare @substidx int declare @pos int declare @saverr int select @subschars = 'abcdefghijklmnopqrstuvwxyz' select @saverr = @@error if (@saverr <> 0) goto EXIT_LABEL select @name_out = @name_in select @saverr = @@error if (@saverr <> 0) goto EXIT_LABEL select @saverr = @@error if (@saverr <> 0) goto EXIT_LABEL select @substidx = 0 select @pos = 3 while exists (select * from tempdb..sysobjects where name = @name_out) begin if @substidx > 25 begin select @pos = @pos + 1 select @substidx = 1 end else select @substidx = @substidx + 1 select @curchar = substring(@subschars, @substidx, 1) select @saverr = @@error if (@saverr <> 0) goto EXIT_LABEL select @name_out = stuff(@name_out, @pos, 1, @curchar) select @saverr = @@error if (@saverr <> 0) goto EXIT_LABEL end return (0) EXIT_LABEL: if (@saverr <> 0) begin RAISERROR(15001, 16, -1, 'sp_MSuniquetempname') return (1) end GO exec dbo.sp_MS_marksystemobject sp_MSuniquetempname go raiserror('Creating procedure sp_MSuniquecolname', 0,1) GO create procedure sp_MSuniquecolname @table_name nvarchar(270), -- this is a qualified_name @base_colname sysname, @unique_colname sysname output as begin set nocount on declare @icol_suffix int select @icol_suffix = ( max(colorder) + 1 ) from syscolumns where id = object_id( @table_name ) select @unique_colname = @base_colname while exists( select * from syscolumns where id = object_id( @table_name ) and name = @unique_colname ) begin select @unique_colname = @base_colname + convert( nvarchar(40), @icol_suffix ) select @icol_suffix = @icol_suffix * ( @@spid + 1 ) end end go exec dbo.sp_MS_marksystemobject sp_MSuniquecolname go raiserror('Creating procedure sp_MSaddguidcolumn', 0,1) GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON go create procedure sp_MSaddguidcolumn @source_owner sysname, @source_table sysname /* table name */ as declare @rowguidcol sysname declare @id int declare @qualified_name nvarchar(270) declare @columns varbinary(128) declare @retcode int declare @colid int declare @pubid uniqueidentifier set nocount on select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_table) select @id = object_id(@qualified_name) /* Alter the source table to add a rowguid column */ begin tran save tran sp_MSaddguidcolumn exec dbo.sp_MSunmarkreplinfo @source_table, @source_owner if @@ERROR <>0 goto UNDO if not exists (select * from syscolumns where ObjectProperty(object_id(@qualified_name), 'tablehasrowguidcol')=1) begin exec dbo.sp_MSuniquecolname @qualified_name, 'rowguid', @rowguidcol output exec ('alter table ' + @qualified_name + ' add ' + @rowguidcol + ' uniqueidentifier ROWGUIDCOL default newid() not null') if @@ERROR<>0 goto UNDO end exec dbo.sp_MSunmarkreplinfo @source_table, @source_owner, 1 if @@ERROR<>0 goto UNDO commit tran return (0) UNDO: rollback tran sp_MSaddguidcolumn commit tran return (1) go exec dbo.sp_MS_marksystemobject sp_MSaddguidcolumn go grant exec on dbo.sp_MSaddguidcolumn to public go SET ANSI_NULLS OFF GO raiserror('Creating procedure sp_MSprepare_mergearticle', 0,1) GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON go create procedure sp_MSprepare_mergearticle @source_owner sysname, @source_table sysname, /* table name */ @publication sysname, @qualified_tablename nvarchar(270) as declare @retcode int exec @retcode = sp_MSaddguidcolumn @source_owner, @source_table if @@error <> 0 or @retcode <> 0 return (1) exec @retcode = sp_MSaddguidindex @publication, @source_owner, @source_table if @@error <> 0 or @retcode <> 0 return (1) exec @retcode = sp_MSaddmergetriggers @qualified_tablename if @@error <> 0 or @retcode <> 0 return (1) go exec dbo.sp_MS_marksystemobject sp_MSprepare_mergearticle go grant exec on dbo.sp_MSprepare_mergearticle to public go SET ANSI_NULLS OFF GO raiserror('Creating procedure sp_MSgetcolumnlist', 0,1) GO create procedure sp_MSgetcolumnlist( @pubid uniqueidentifier, @column_list nvarchar(4000) OUTPUT, @source_objid int, @guid_alias sysname=NULL ) AS /* ** if it is not vertically partitioned, then get all columns ** else get the column list as given in columns of sysmergearticles */ declare @bitset int declare @columns varbinary(125) declare @setcolcnt int declare @colcnt int declare @colname nvarchar(140) declare @colid int declare @unqual_sourcename sysname declare @qual_sourcename nvarchar(140) if @guid_alias is NULL begin select @unqual_sourcename=object_name(@source_objid) select @qual_sourcename = QUOTENAME(@unqual_sourcename) end else select @qual_sourcename=@guid_alias select @columns = columns from sysmergearticles where objid = @source_objid and pubid=@pubid select @column_list = '' select @setcolcnt = 0 select @colcnt = count(*) from syscolumns where id = @source_objid declare collist CURSOR LOCAL FAST_FORWARD FOR select name, colid from syscolumns where id = @source_objid order by colid ASC FOR READ ONLY open collist fetch collist into @colname, @colid WHILE (@@fetch_status <> -1) BEGIN exec @bitset = sp_MStestbit @columns, @colid -- if @bitset<>0 if @bitset<>0 or @columns is null or @columns = '' begin select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid' if @column_list='' --which can cause problems and is not necessary. select @column_list = @qual_sourcename + '.' + @colname else select @column_list = @column_list + ', ' + @qual_sourcename + '.' + @colname select @setcolcnt = @setcolcnt + 1 end fetch next from collist into @colname, @colid END close collist deallocate collist if @setcolcnt = @colcnt select @column_list = ' * ' GO exec dbo.sp_MS_marksystemobject sp_MSgetcolumnlist go grant exec on dbo.sp_MSgetcolumnlist to public go raiserror('Creating procedure sp_MSaddguidindex', 0,1) GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON go create procedure sp_MSaddguidindex @publication sysname, @source_owner sysname, @source_table sysname as set nocount on declare @indexname nvarchar(270) declare @colname nvarchar(270) declare @retcode int declare @qualified_name nvarchar(270) declare @colid int declare @columns varbinary(125) declare @id int declare @pubid uniqueidentifier declare @mergepublish int select @mergepublish = 0x4000 select @pubid = pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name() select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_table) select @id = object_id(@qualified_name) select @colid=colid from syscolumns where id=@id and columnproperty(@id, name, 'isrowguidcol')=1 update syscolumns set colstat=colstat | @mergepublish where id=@id and colid=@colid if @@ERROR<>0 return (1) /* Update the columns bitmaps in all articles at the current publisher that share the same table */ declare artpubs CURSOR LOCAL FAST_FORWARD FOR select DISTINCT pubid from sysmergearticles where objid=@id and pubid in ( select pubid from sysmergepublications where UPPER(publisher) = UPPER(@@SERVERNAME) and publisher_db = db_name()) FOR READ ONLY open artpubs fetch next from artpubs into @pubid while (@@fetch_status <> -1) begin select @columns=columns from sysmergearticles where objid=@id and pubid=@pubid exec @retcode = sp_MSsetbit @columns OUTPUT, @colid if @retcode<>0 or @@ERROR<>0 return (1) update sysmergearticles set columns=@columns where objid=@id and pubid = @pubid if @@ERROR<>0 return (1) fetch next from artpubs into @pubid end close artpubs deallocate artpubs select @indexname = 'index_' + convert(nvarchar(36), @id) /* Make sure index name is unique */ exec @retcode = dbo.sp_MSuniqueobjectname @indexname, @indexname output if @retcode <>0 return (1) select @colname=name from syscolumns where id=@id and columnproperty(@id, name, 'isrowguidcol')=1 if (@colname is null) return (1) /* Alter the source table to add a rowguid column */ if (not exists (select * from sysindexes where id = object_id(@qualified_name) and @colname = index_col(@qualified_name, indid, 1) and indexproperty(id, name, 'IsUnique') = 1 and index_col(@qualified_name, indid, 2) is null)) begin select @colname = N'[' + replace(@colname, N']', N']]') + N']' exec ('create unique index ' + @indexname + ' on ' + @qualified_name + N' (' + @colname + N')') if @@ERROR <>0 return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSaddguidindex go grant exec on dbo.sp_MSaddguidindex to public go SET ANSI_NULLS OFF raiserror('Creating procedure sp_MSrefcnt', 0,1) GO create procedure sp_MSrefcnt @objid int, @refcnt int output as set @refcnt = 0 create table #temprefs (objid int NOT NULL) insert into #temprefs select distinct rkeyid from sysreferences where fkeyid = @objid while (@@rowcount > 0) insert into #temprefs select distinct rkeyid from sysreferences where fkeyid in (select objid from #temprefs) and rkeyid not in (select objid from #temprefs) select @refcnt = count(*) from #temprefs return (0) GO exec dbo.sp_MS_marksystemobject sp_MSrefcnt go raiserror('Creating procedure sp_MSgentablenickname', 0,1) GO create procedure sp_MSgentablenickname @tablenick int output, @nickname int, @objid int as declare @refcnt int declare @retval int /* Create a tablenickname from the following formula ** 1. Get the refcnt, use it for the high order digits so ** that processing inserts by ascending tablenickname works well ** and processing deletes by descending tablenickname works well. ** 2. Use a couple of digits from the nickname so that it is less likely ** to collide with tablenickname generated at another merge publisher. ** 3. Increment as necessary to make it unique within the publication and database. */ exec @retval = dbo.sp_MSrefcnt @objid, @refcnt output if @retval <> 0 return (1) if @nickname < 0 set @nickname = 0 - @nickname /* the biggest value for int is 2147483647, we therefore use another algorithm when ** @refcnt is bigger than 200 */ if @refcnt < 200 begin set @tablenick = 1000 * ((@refcnt * 10000) + (@nickname % 10000)) while exists (select * from sysmergearticles where nickname = @tablenick) set @tablenick = @tablenick + 1 end else select @tablenick=max(nickname) + 1 from sysmergearticles return (0) go exec dbo.sp_MS_marksystemobject sp_MSgentablenickname go raiserror('Creating procedure sp_MStablenickname', 0,1) GO create procedure sp_MStablenickname @owner sysname, @tablename sysname, @nick int output as declare @qualified_name nvarchar(270) if @owner is not null select @qualified_name = QUOTENAME(@owner) + '.' + QUOTENAME(@tablename) else select @qualified_name = QUOTENAME(@tablename) select @nick = nickname from sysmergearticles a, sysobjects o where a.objid = o.id and o.id = OBJECT_ID(@qualified_name) and (user_name(uid) = @owner or @owner is null) if @nick is NULL return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MStablenickname go raiserror('Creating procedure sp_MStablenamefromnick', 0,1) GO create procedure sp_MStablenamefromnick @nick int, @tablename nvarchar(270) output, @pubid uniqueidentifier = NULL as declare @owner sysname declare @table sysname if (@pubid is null) select @table = name, @owner = user_name(uid) from sysobjects where id in (select objid from sysmergearticles where nickname = @nick) else select @table = name, @owner = user_name(uid) from sysobjects where id in (select objid from sysmergearticles where nickname = @nick and pubid = @pubid) select @tablename = QUOTENAME(@owner) + '.' + QUOTENAME(@table) if (@table is NULL) or (@owner is NULL) begin raiserror(21124, 16, -1, @nick) return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MStablenamefromnick go raiserror('Creating procedure sp_MSgetmakegenerationapplock', 0,1) GO create procedure sp_MSgetmakegenerationapplock @head_of_queue int OUTPUT as set nocount on declare @retcode smallint declare @lock_resource nvarchar(255) select @retcode = 0 select @head_of_queue = 0 select @lock_resource = N'MSinternal_makegeneration_inprogress' + convert(nvarchar(11), db_id()) -- If someone else is making generations / has just made one, exit so -- that we won't deadlock exec @retcode = sp_getapplock @Resource = @lock_resource, @LockMode = N'Exclusive', @LockOwner = N'Session', @LockTimeout = 0 -- do not wait at all. if @@error <> 0 or @retcode < 0 begin -- If the previous applock request timed out, then try again, this time using the default timeout. -- Note that even though the -1 lock timeout means indefinite timeout, the query timeout set on the -- session overrides the lock timeout. So it's okay even if the default @@lock_timeout is -1. if (@retcode = -1) begin exec @retcode = sp_getapplock @Resource = @lock_resource, @LockMode = N'Exclusive', @LockOwner = N'Session' -- If we succeeded to get the lock, set @lock_acquired to 1 so that the exit code releases the lock. if (@retcode >= 0) begin goto EXIT_PROC end end end else begin select @head_of_queue = 1 end EXIT_PROC: return @retcode go exec dbo.sp_MS_marksystemobject sp_MSgetmakegenerationapplock go grant exec on dbo.sp_MSgetmakegenerationapplock to public go raiserror('Creating procedure sp_MSreleasemakegenerationapplock', 0,1) GO create procedure sp_MSreleasemakegenerationapplock as set nocount on declare @retcode smallint declare @lock_resource nvarchar(255) select @retcode = 0 select @lock_resource = N'MSinternal_makegeneration_inprogress' + convert(nvarchar(11), db_id()) exec @retcode = sp_releaseapplock @Resource = @lock_resource, @LockOwner = N'Session' if @@error <> 0 or @retcode < 0 return (1) else return (0) go exec dbo.sp_MS_marksystemobject sp_MSreleasemakegenerationapplock go grant exec on dbo.sp_MSreleasemakegenerationapplock to public go raiserror('Creating procedure sp_MSmakegeneration', 0,1) GO create procedure sp_MSmakegeneration @gencheck int = 0 as set nocount on declare @gen int declare @nick int declare @genguid uniqueidentifier declare @dt datetime declare @dt2 datetime declare @art_nick int declare @first_ts int declare @makenewrow bit declare @retcode smallint declare @guidnull uniqueidentifier declare @nickbin varbinary(255) declare @maxgendiff_fornewrow int declare @count_of_articles int declare @lock_acquired bit declare @lock_resource nvarchar(255) declare @procfailed bit declare @delete_old_genhistory bit declare @close_old_genhistory bit declare @localize_zeroartnick_generations bit select @procfailed = 1 select @retcode = 0 set @guidnull = '00000000-0000-0000-0000-000000000000' /* ** Check to see if current publication has permission */ exec @retcode=sp_MSreplcheck_connection if @retcode<>0 or @@ERROR<>0 goto EXIT_PROC set @genguid = newid() exec @retcode=sp_MSgetreplnick @nickname = @nick out if @retcode<>0 or @@error<>0 goto EXIT_PROC -- convert @nick into binary and add a guard byte if needed if @nick % 256 = 0 set @nickbin = convert(binary(4), @nick) + 0x01 else set @nickbin = convert(binary(4), @nick) select @dt2 = max(coldate) from dbo.MSmerge_genhistory where guidsrc = guidlocal set @dt = getdate() if datediff(dd, @dt2, @dt) = 0 begin if 500 > datediff(ms, @dt2, @dt) and 0 < datediff(ms, @dt2, @dt) begin select @procfailed = 0 goto EXIT_PROC end end if @gencheck = 3 set @localize_zeroartnick_generations = 1 else set @localize_zeroartnick_generations = 0 -- localize interrupted generations exec @retcode= sp_MSlocalizeinterruptedgenerations @localize_zeroartnick_generations = @localize_zeroartnick_generations if @retcode<>0 or @@error<>0 goto EXIT_PROC -- If @gencheck param is set to 1 ( = ForceConvergence), look for rows with missing generation numbers and set their -- gen to 0 if @gencheck = 1 or @gencheck = 2 begin update dbo.MSmerge_contents set generation = 0 where generation not in (select generation from dbo.MSmerge_genhistory) update dbo.MSmerge_tombstone set generation = 0 where generation not in (select generation from dbo.MSmerge_genhistory) end /* ** If there are no zero generation tombstones or rows, add a dummy row in there. */ if not exists (select * from dbo.MSmerge_genhistory) begin insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values (@genguid, @genguid, 1, 0, @nickbin, @dt) if (@@error <> 0) goto EXIT_PROC end select @art_nick = min(nickname), @count_of_articles = count(*) from sysmergearticles -- Calculate how much less than the max generation in MSmerge_genhistory are we willing to have the minimum open generation in MSmerge_genhistory. -- Having a number smaller than or roughly equal to the number of articles will cause more aggressive closing of existing open gens (and making new rows) with 0 changes -- and hence more generations for merge agents to deal with. Having a very high number will cause less aggressive closing of open gens but will cause the -- common gens of replicas to be stuck at lower numbers because of the existence of "holes" at much lower gen values. An optimization that works well -- and is a compromise between the two extremes is to have the max of 100 or (2 * @count_of_articles) + 1 as the max diff we allow before deciding to make a new row. if ((2 * @count_of_articles) + 1) > 100 select @maxgendiff_fornewrow = (2 * @count_of_articles) + 1 else select @maxgendiff_fornewrow = 100 while @art_nick is not null begin declare @cmd nvarchar(200) declare @old_bi_gen int declare @bi_objid int set @old_bi_gen= NULL -- if @old_bi_gen stays NULL: no need to move bi-rows set @bi_objid= NULL set @delete_old_genhistory = 0 set @close_old_genhistory = 0 set @makenewrow = 0 set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @art_nick) if @bi_objid is not null begin set @cmd= 'update dbo.' + object_name(@bi_objid) + ' set generation= @gen where generation = @oldgen' end begin tran save tran sp_MSmakegeneration select @gen = max(gen_cur) from sysmergearticles (updlock holdlock) where nickname = @art_nick and gen_cur is not null -- if either we have no gen_cur set yet, or if we have one but no corresponding genhistory row or we have a closed one which -- was bcp-ed in after a reinit, we need to create a new one. if @gen is null or ( @gen is not null and not exists (select generation from dbo.MSmerge_genhistory where generation = @gen and guidlocal = @guidnull) ) begin declare @oldgen int declare @maxgencur int set @genguid = newid() set @oldgen = @gen select @gen = COALESCE(1 + max(generation), 1) from dbo.MSmerge_genhistory (updlock) -- Make sure that the new generation value is not smaller than any existing sysmergearticles.gen_cur. select @maxgencur = isnull(max(gen_cur),0) from sysmergearticles where gen_cur is not null if (@gen <= @maxgencur) begin set @gen = @maxgencur + 1 -- Now we are guaranteed to not collide with an existing gen_cur end insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values(@genguid, @guidnull, @gen, @art_nick, @nickbin, @dt) if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update sysmergearticles set gen_cur = @gen where nickname = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end -- if this was the case of a gen_cur with no matching genhistory rows, then update the contents and tombstones rows with the new generation value. if @oldgen is not null begin update dbo.MSmerge_contents set generation = @gen, partchangegen = @gen, joinchangegen = @gen where generation = @oldgen and partchangegen = @oldgen and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update dbo.MSmerge_contents set generation = @gen, joinchangegen = @gen where generation = @oldgen and joinchangegen = @oldgen and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update dbo.MSmerge_contents set generation = @gen where generation = @oldgen and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update dbo.MSmerge_tombstone set generation = @gen where generation = @oldgen and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end if @bi_objid is not null begin exec dbo.sp_executesql @cmd, N'@gen int, @oldgen int', @gen= @gen, @oldgen= @oldgen if @@ERROR <> 0 goto EXIT_RELEASE_TRAN end end end -- these updates should be hitting zero rows... if exists (select * from dbo.MSmerge_contents (readpast readcommitted) where generation = 0 and tablenick = @art_nick) begin update dbo.MSmerge_contents set generation = @gen, partchangegen = @gen, joinchangegen = @gen where generation = 0 and partchangegen = 0 and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update dbo.MSmerge_contents set generation = @gen, joinchangegen = @gen where generation = 0 and joinchangegen = 0 and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update dbo.MSmerge_contents set generation = @gen where generation = 0 and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end end if exists (select * from dbo.MSmerge_tombstone (readpast readcommitted) where generation = 0 and tablenick = @art_nick) begin update dbo.MSmerge_tombstone set generation = @gen where generation = 0 and tablenick = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end end if not exists (select * from dbo.MSmerge_contents where tablenick = @art_nick and generation = @gen) and not exists (select * from dbo.MSmerge_tombstone where tablenick = @art_nick and generation = @gen) begin select @dt2 = coldate from dbo.MSmerge_genhistory where generation = @gen if datediff(dd, @dt2, @dt) = 0 and not exists (select * from dbo.MSmerge_genhistory where generation > @maxgendiff_fornewrow + @gen) begin set @makenewrow = 0 set @delete_old_genhistory = 0 -- If @gencheck param is set to 3 (= OverrideMakeNewGenerations), set the @makenewrow flag -- This is done for message based merges to ensure that the incomplete gens always get closed -- during every merge if there are completed generations > than this one. -- Besides closed generations > this one, we also need to watch out for open generations > this one -- that have pending changes, and hence sp_MSmakegeneration will eventually close them out. This is -- required when the @gen just happens to be processed before other open generations with changes. if @gencheck = 3 begin if exists (select * from dbo.MSmerge_genhistory gh where gh.generation > @gen and gh.guidlocal <> @guidnull) or exists (select * from dbo.MSmerge_contents mc where mc.generation > @gen) or exists (select * from dbo.MSmerge_tombstone mt where mt.generation > @gen) begin set @makenewrow = 1 set @old_bi_gen= @gen -- we will move bi-rows set @delete_old_genhistory = 1 end end end else begin set @makenewrow = 1 set @old_bi_gen= @gen -- we will move bi-rows set @delete_old_genhistory = 1 end end else begin set @makenewrow = 1 set @delete_old_genhistory = 0 -- don't delete existing genhistory row. just mark it as closed. set @close_old_genhistory = 1 end if (@makenewrow = 1) begin declare @newgen int /* reset next generation for this article */ set @genguid = newid() insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) select @genguid, @guidnull, COALESCE(1 + max(generation), 1), @art_nick, @nickbin, @dt from dbo.MSmerge_genhistory (updlock) if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end select @newgen = generation from dbo.MSmerge_genhistory where guidsrc = @genguid update sysmergearticles set gen_cur = @newgen where nickname = @art_nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end if @bi_objid is not null and @old_bi_gen is not NULL begin exec dbo.sp_executesql @cmd, N'@gen int, @oldgen int', @gen = @newgen, @oldgen = @old_bi_gen if @@ERROR <> 0 goto EXIT_RELEASE_TRAN end end if (@delete_old_genhistory = 1) begin declare @error int declare @genhistory_rowsdeleted int -- delete the old genhistory row only if there still aren't any rows in contents or -- tombstone with this generation value. Note that after the previous update statement on sysmergearticles -- no new spids can take locks on sysmergearticles and hence cannot -- insert any new rows with the old gen_cur. delete from dbo.MSmerge_genhistory where generation = @gen and not exists (select * from dbo.MSmerge_contents where tablenick = @art_nick and generation = @gen) and not exists (select * from dbo.MSmerge_tombstone where tablenick = @art_nick and generation = @gen) select @genhistory_rowsdeleted = @@rowcount, @error = @@error -- If the genhistory row which we previously thought could be deleted, now has changes in contents or -- tombstone, it's okay to not delete it and still leave it as open. In future this open generation -- will be treated as an interrupted generation and the changes in it will be moved to a new local -- generation. So there will be convergence. Deleting the genhistory row based on incorrect past determination -- of 0 changes is dangerous and can easily cause non-convergence. -- The best solution is to close this generation if we finally didn't delete it. The reason is that it -- allows subscribers to move their last received watermark higher than this open generation. if @genhistory_rowsdeleted = 0 begin set @close_old_genhistory = 1 end if (@error <> 0) begin goto EXIT_RELEASE_TRAN end end if (@close_old_genhistory = 1) begin set @genguid = newid() update dbo.MSmerge_genhistory set guidsrc = @genguid, guidlocal = @genguid, coldate = @dt where generation = @gen if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end update MSmerge_replinfo set recgen = @gen, recguid = @genguid, sentgen = @gen, sentguid = @genguid where replnickname = @nick if (@@error <> 0) begin goto EXIT_RELEASE_TRAN end end commit transaction -- set up for next time through the loop select @art_nick = min(nickname) from sysmergearticles where nickname > @art_nick set @dt = getdate() end select @procfailed = 0 EXIT_RELEASE_TRAN: if (@procfailed = 1) begin rollback tran sp_MSmakegeneration commit transaction end EXIT_PROC: if (@procfailed = 1) return (1) else return (0) go exec dbo.sp_MS_marksystemobject sp_MSmakegeneration go grant exec on dbo.sp_MSmakegeneration to public go raiserror('Creating procedure sp_MSfixlineageversions', 0,1) GO create procedure sp_MSfixlineageversions as declare @lNick int -- length of nickname in bytes declare @lVer int -- length of version in bytes declare @lEntry int -- length of entry in bytes declare @cEntries int declare @idx int -- @idx is the index of the entry that potentially needs to be bumped up set @lNick= 4 set @lVer= 4 set @lEntry= @lNick + @lVer set @cEntries= 31 -- max number of entries if exists (select * from sysobjects where type = 'U' and name = 'MSmerge_contents') begin -- starting at the end of the lineage, replace every lower version by the following higher version set @idx= @cEntries - 2 -- lineage entries range from 0..30; compare 30 with 29, then 29 with 28, ..., 1 with 0 while @idx >= 0 begin update dbo.MSmerge_contents set lineage= -- leave everything before "entry[@idx].version" unmodified substring(lineage, 1, @idx * @lEntry + @lNick) + -- replace "entry[@idx].version" by "entry[@idx+1].version" substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, @lVer) + -- leave everything starting at "entry[@idx+1].nickname" unmodified substring(lineage, (@idx + 1) * @lEntry + 1, (@cEntries * @lEntry) - ((@idx + 1) * @lEntry) + 1) -- '+1' is for trailing byte where -- skip this step right away for shorter lineages datalength(lineage) >= @lEntry * (@idx + 2) and ( -- either least significant version byte at second entry is higher, the other three are equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, 1) > substring(lineage, @idx * @lEntry + @lNick + 1, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 3) = substring(lineage, @idx * @lEntry + @lNick + 2, 3)) or -- 2nd least significant version byte at second entry is higher, the most and 2nd most significant bytes are equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 1) > substring(lineage, @idx * @lEntry + @lNick + 2, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 2) = substring(lineage, @idx * @lEntry + @lNick + 3, 2)) or -- 3rd least significant version byte at second entry is higher, the most significant byte is equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 1) > substring(lineage, @idx * @lEntry + @lNick + 3, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) = substring(lineage, @idx * @lEntry + @lNick + 4, 1)) or -- most significant byte at second entry is higher substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) > substring(lineage, @idx * @lEntry + @lNick + 4, 1) ) if @@error<>0 begin return (1) end set @idx= @idx - 1 end end if exists (select * from sysobjects where type = 'U' and name = 'MSmerge_tombstone') begin -- starting at the end of the lineage, replace every lower version by the following higher version set @idx= @cEntries - 2 -- lineage entries range from 0..30; compare 30 with 29, then 29 with 28, ..., 1 with 0 while @idx >= 0 begin update dbo.MSmerge_tombstone set lineage= -- leave everything before "entry[@idx].version" unmodified substring(lineage, 1, @idx * @lEntry + @lNick) + -- replace "entry[@idx].version" by "entry[@idx+1].version" substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, @lVer) + -- leave everything starting at "entry[@idx+1].nickname" unmodified substring(lineage, (@idx + 1) * @lEntry + 1, (@cEntries * @lEntry) - ((@idx + 1) * @lEntry) + 1) -- '+1' is for trailing byte where -- skip this step right away for shorter lineages datalength(lineage) >= @lEntry * (@idx + 2) and ( -- either least significant version byte at second entry is higher, the other three are equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, 1) > substring(lineage, @idx * @lEntry + @lNick + 1, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 3) = substring(lineage, @idx * @lEntry + @lNick + 2, 3)) or -- 2nd least significant version byte at second entry is higher, the most and 2nd most significant bytes are equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 1) > substring(lineage, @idx * @lEntry + @lNick + 2, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 2) = substring(lineage, @idx * @lEntry + @lNick + 3, 2)) or -- 3rd least significant version byte at second entry is higher, the most significant byte is equal (substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 1) > substring(lineage, @idx * @lEntry + @lNick + 3, 1) and substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) = substring(lineage, @idx * @lEntry + @lNick + 4, 1)) or -- most significant byte at second entry is higher substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) > substring(lineage, @idx * @lEntry + @lNick + 4, 1) ) if @@error<>0 begin return (1) end set @idx= @idx - 1 end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSfixlineageversions go grant exec on dbo.sp_MSfixlineageversions to public go dump tran master with no_log go raiserror('Creating procedure sp_MSaddupdatetrigger', 0,1) GO CREATE PROCEDURE sp_MSaddupdatetrigger @source_table nvarchar(270), /* source table name */ @owner sysname, /* Owner name of source table */ @object sysname, /* Object name */ @artid uniqueidentifier, /* Article id */ @column_tracking int, @viewname sysname /* name of view on syscontents */ AS declare @command1 nvarchar(4000) declare @command2 nvarchar(4000) declare @command3 nvarchar(4000) declare @command4 nvarchar(4000) declare @inscommand nvarchar(2000) declare @tablenick int declare @nickname int declare @viewcols int declare @trigname sysname declare @ext nvarchar(10) declare @gstr sysname declare @tablenickchar nvarchar(11) declare @ccols int declare @guidstr nvarchar(32) declare @colid smallint declare @colordinal smallint declare @colordstr varchar(4) declare @colname sysname declare @cur_name sysname declare @colpat nvarchar(130) declare @colchar nvarchar(5) declare @piece nvarchar(400) declare @retcode int declare @ifcol nvarchar(4000) declare @ccolchar nvarchar(5) declare @partchangecnt int declare @joinchangecnt int declare @partchangecnt2 int declare @cvstr1 nvarchar(500) declare @cvstr2 nvarchar(500) declare @flag smallint declare @missingbm varbinary(500) declare @missing_cols varbinary(32) declare @mapdownbm varbinary(500) declare @mapupbm varbinary(500) declare @missingcolid int declare @maxcolid int declare @missingbmstr varchar(1000) declare @mapdownbmstr varchar(1000) declare @mapupbmstr varchar(1000) declare @objid int declare @sync_objid int declare @partchbm varbinary(500) declare @missing_col_count int declare @excluded_col_count int declare @joinchbm varbinary(500) declare @partchstr varchar(1002) declare @joinchstr varchar(1002) declare @column_hole bit declare @notforrepl_bit bit declare @notforrepl_str nvarchar(200) declare @owner_is_admin bit set @notforrepl_bit = 1 set @ifcol = '' set @column_hole = 0 set @owner_is_admin = 0 select @owner_is_admin=sysadmin from master..syslogins l, sysusers u where l.sid=u.sid and u.name=@owner collate database_default select @flag = 0 set @objid = OBJECT_ID(@source_table) select @sync_objid = sync_objid, @missing_cols = missing_cols, @excluded_col_count = excluded_col_count, @missing_col_count=missing_col_count from sysmergearticles where artid= @artid and objid=@objid select @ccols = count(*) from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' /* Figure out if there are any holes in the colid sequence */ select @maxcolid = max(colid) from syscolumns where id = @objid if @ccols <> @maxcolid select @column_hole = 1 /* ** adjust the number of columns in the original table by adding up missing columns; in both Pub/Sub sides. */ if @missing_col_count>0 select @ccols = @ccols + @missing_col_count select @ccolchar = convert(nchar, @ccols) set @colordinal = 0 execute @retcode=sp_MStablenickname @owner, @object, @tablenick output if @@ERROR<>0 or @retcode<>0 return (1) set @tablenickchar = convert(nchar, @tablenick) set @joinchbm = 0x0 set @partchbm = 0x0 -- Check if the update trigger can be made NOT FOR REPLICATION if exists (select * from sysmergearticles where nickname = @tablenick and (before_image_objid is not null or before_view_objid is not null or datalength (subset_filterclause) > 1 )) begin select @notforrepl_bit = 0 end else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick or join_nickname = @tablenick) begin select @notforrepl_bit = 0 end else begin select @notforrepl_bit = 1 end select @notforrepl_str = ' if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1 -- and master.dbo.fn_isreplmergeagent() = 1 return ' declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, colid from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid FOR READ ONLY /* Try to set the ifcol pieces of the trigger */ open col_cursor fetch next from col_cursor into @colname, @colid while (@@fetch_status <> -1) begin set @colordinal = @colordinal + 1 set @colpat = '%' + @colname + '%' /* Don't let them update the rowguid column */ if columnproperty( @objid, @colname , 'isrowguidcol')=1 set @ifcol = 'if update(' + QUOTENAME(@colname) + ') begin if @@trancount > 0 rollback tran RAISERROR (20062, 16, -1) end ' /* does updating this column change membership in a partial replica? */ select @partchangecnt = count(*) from sysmergearticles where nickname = @tablenick and subset_filterclause like @colpat select @partchangecnt2 = count(*) from sysmergesubsetfilters where art_nickname = @tablenick and join_filterclause like @colpat select @joinchangecnt = count(*) from sysmergesubsetfilters where join_nickname = @tablenick and join_filterclause like @colpat if @partchangecnt > 0 or @partchangecnt2 > 0 exec dbo.sp_MSsetbit @partchbm out, @colid else if @joinchangecnt > 0 exec dbo.sp_MSsetbit @joinchbm out, @colid /* Repeat the loop with next column */ fetch next from col_cursor into @colname, @colid end close col_cursor deallocate col_cursor -- Initialize string for inserting to before_image table exec sp_MSgetbeforetableinsert @objid, @inscommand output /* Make strings to initialize variables for partchange, joinchange bitmaps */ exec master..xp_varbintohexstr @partchbm, @partchstr out exec master..xp_varbintohexstr @joinchbm, @joinchstr out select @mapdownbm =0x00 select @mapupbm = 0x00 /* ** To see if there is a need for map down. */ if @column_hole<>0 begin set @missingcolid = 1 while (@missingcolid <= @maxcolid) begin if not exists (select * from syscolumns where colid = @missingcolid and id = OBJECT_ID(@source_table) and iscomputed <> 1 and type_name(xtype) <> 'timestamp') exec dbo.sp_MSsetbit @mapdownbm out, @missingcolid set @missingcolid = @missingcolid + 1 end end set @mapupbm = @missing_cols -- do this at both sides, good for republishing. exec master..xp_varbintohexstr @mapdownbm, @mapdownbmstr out exec master..xp_varbintohexstr @mapupbm, @mapupbmstr out execute @retcode=sp_MSgetreplnick @nickname = @nickname output if @retcode<>0 or @@error<>0 return (1) set @ext = 'upd_' exec @retcode=sp_MSguidtostr @artid, @guidstr out if @retcode<>0 or @@error<>0 return (1) set @trigname = @ext + @guidstr /* Make sure trigger name is unique */ exec @retcode=sp_MSuniqueobjectname @trigname, @trigname output if @retcode<>0 or @@error<>0 return (1) if @column_tracking <> 0 begin /* Set cv pieces appropriately */ set @cvstr1 = ' set @lineage = { fn UPDATELINEAGE(0x0, @nick, @oldmaxversion+1) } set @cv = { fn INITCOLVS(@ccols, @nick) } if (@@error <> 0) begin goto FAILURE end set @cv = { fn UPDATECOLVBM(@cv, @nick, @bm, @missingbm, { fn GETMAXVERSION(@lineage) }) } ' set @cvstr2 = ' colv1 = { fn UPDATECOLVBM(colv1, @nick, @bm, @missingbm, { fn GETMAXVERSION({ fn UPDATELINEAGE(lineage, @nick, @oldmaxversion+1) }) }) } ' end else begin set @cvstr1 = ' set @lineage = { fn UPDATELINEAGE(0x0, @nick, @oldmaxversion+1) } set @cv = NULL ' set @cvstr2 = ' colv1 = NULL ' end /* UNDONE maybe remove null guid checks in SQL SERVER 7.0 */ select @command1 = 'create trigger ' + @trigname + ' on ' + @source_table + ' FOR UPDATE AS ' if (@notforrepl_bit = 1) select @command1 = @command1 + @notforrepl_str select @command1 = @command1 + ' /* Declare variables */ declare @article_rows_updated int select @article_rows_updated = count(*) from inserted declare @contents_rows_updated int, @updateerror int declare @bm varbinary(500), @missingbm varbinary(500), @lineage varbinary(255), @cv varbinary(2048) declare @tablenick int, @nick int, @ccols int, @partchange int, @joinchange int declare @partchangebm varbinary(500), @joinchangebm varbinary(500) declare @oldmaxversion int set nocount on set @tablenick = ' + @tablenickchar + ' select @oldmaxversion= maxversion_at_cleanup from sysmergearticles where nickname = @tablenick /* Use intrinsic funtion to set bits for updated columns */ set @bm = columns_updated() /* only do the map down when needed */ set @missingbm = ' select @command2 = ' /* See if the partition might have changed */ if @partchangebm = 0x0 set @partchange = 0 else set @partchange= { fn INTERSECTBITMAPS (@bm, @partchangebm) } /* See if a column used in a join filter changed */ if @joinchangebm = 0x0 set @joinchange = 0 else set @joinchange= { fn INTERSECTBITMAPS (@bm, @joinchangebm) } ' if @mapdownbm<>0x00 select @command2 = @command2 + ' execute master..xp_mapdown_bitmap ' + @mapdownbmstr +', @bm output ' select @command2 = @command2 + ' exec dbo.sp_MSgetreplnick @nickname = @nick output select @ccols = ' + @ccolchar + ' ' + @cvstr1 + ' ' set @command3 = ' update ' + @viewname + ' set lineage = { fn UPDATELINEAGE(lineage, @nick, @oldmaxversion+1) }, generation = A.gen_cur, joinchangegen = case when (@joinchange = 1) then A.gen_cur else joinchangegen end, partchangegen = case when (@partchange = 1) then A.gen_cur else partchangegen end, ' + @cvstr2 + ' FROM inserted as I JOIN ' + @viewname + ' as V ON (I.rowguidcol=V.rowguid) and V.tablenick = @tablenick JOIN (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as A ON V.tablenick = A.nickname select @updateerror = @@error, @contents_rows_updated = @@rowcount ' + case when @inscommand is null or @inscommand = ' ' then ' ' else ' if @joinchange = 1 or @partchange = 1 ' + @inscommand end + ' if @article_rows_updated <> @contents_rows_updated begin insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, partchangegen, joinchangegen) select @tablenick, rowguidcol, @lineage, @cv, A.gen_cur, case when (@joinchange = 1 or @partchange = 1) then A.gen_cur else NULL end, case when @joinchange = 1 then A.gen_cur else NULL end from inserted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as A where rowguidcol not in (select rowguid from ' + @viewname + ' where tablenick = @tablenick) if @@error <> 0 GOTO FAILURE end return FAILURE: if @@trancount > 0 rollback tran raiserror (20041, 16, -1) return ' execute (@command1 + @mapupbmstr + ' set @partchangebm = ' + @partchstr + ' set @joinchangebm = ' + @joinchstr + ' ' + @ifcol + @command2 + @command3) if @@ERROR <> 0 begin raiserror(20064, 16, -1) return (1) end select @command4 = 'sp_MS_marksystemobject ''' + REPLACE(@owner, '''', '''''') + '.' + @trigname + '''' if @owner_is_admin=1 execute (@command4) GO exec dbo.sp_MS_marksystemobject sp_MSaddupdatetrigger go raiserror('Creating procedure sp_MSaddmergetriggers', 0,1) GO CREATE PROCEDURE sp_MSaddmergetriggers @source_table nvarchar(270), /* was type varchar(92), table name */ @table_owner sysname = NULL, @column_tracking int = NULL /* Is column tracking on - default is FALSE */ AS set nocount on declare @command nvarchar(4000) declare @command2 nvarchar(4000) declare @inscommand nvarchar(2000) declare @ifcoltracking nvarchar(255) declare @tablenick int declare @nickname int declare @artid uniqueidentifier declare @guidstr nvarchar(32) declare @owner sysname declare @site sysname declare @db sysname declare @object sysname declare @updtrigname sysname declare @instrigname sysname declare @deltrigname sysname declare @ext nvarchar(10) declare @ext2 nvarchar(10) declare @tablenickchar nvarchar(11) declare @missing_col_count int declare @ccols int declare @ccolchar nvarchar(5) declare @retcode int declare @objid int declare @bitmap varbinary(40) declare @missing_count int declare @viewname sysname declare @tsview sysname declare @sync_objid int declare @command3 nvarchar(4000) declare @notforrepl_str nvarchar(200) declare @notforrepl_bit bit declare @owner_is_admin bit set @notforrepl_bit = 1 set @owner_is_admin = 0 set @bitmap = 0x0 set @missing_count = 0 -- PARSENAME VARS declare @UnqualName sysname --rightmost name node declare @QualName1 sysname -- END PARSENAME VARS execute @retcode=sp_MSgetreplnick @nickname = @nickname output if @retcode<>0 or @@ERROR<>0 return (1) select @ext = 'ins_' select @ext2 = 'del_' if @table_owner is not NULL select @source_table=QUOTENAME(@table_owner) + '.' + QUOTENAME(@source_table) set @objid = OBJECT_ID(@source_table) -- set up the before image table if one is desired exec dbo.sp_MScreatebeforetable @objid -- Initialize string for inserting to before_image table exec sp_MSgetbeforetableinsert @objid, @inscommand output select @ccols = count(*) from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' select @missing_col_count=missing_col_count, @sync_objid=sync_objid from sysmergearticles where objid=@objid /* ** increase the # of columns at subscriber side by adding the missing columns ** Note that publisher side also needs to increase this value due to the possible column holes. */ select @ccols = @ccols + @missing_col_count select @ccolchar = convert(nchar, @ccols) select @UnqualName = PARSENAME(@source_table, 1) select @QualName1 = PARSENAME(@source_table, 2) if @UnqualName IS NULL return 1 if @QualName1 is NULL select @QualName1 = user_name(uid) from sysobjects where id = object_id(@UnqualName) -- fixup for variable length differences. remove when vars expanded -- to new SQL SERVER 7.0 lengths select @owner = @QualName1 select @object = @UnqualName --this is to find out if table owner is a system user select @owner_is_admin=sysadmin from master..syslogins l, sysusers u where l.sid=u.sid and u.name=@owner collate database_default execute @retcode=sp_MStablenickname @owner, @object, @tablenick output if @retcode<>0 or @@ERROR<>0 return (1) select @artid = artid from sysmergearticles where objid = @objid /* If column tracking wasn't passed in, just figure it out */ if @column_tracking is null select @column_tracking = column_tracking from sysmergearticles where artid = @artid select @tablenickchar = convert(nchar, @tablenick) exec @retcode=sp_MSguidtostr @artid, @guidstr out if @retcode<>0 or @@ERROR<>0 return (1) -- Check if the triggers can be made NOT FOR REPLICATION if exists (select * from sysmergearticles where nickname = @tablenick and (before_image_objid is not null or before_view_objid is not null or datalength (subset_filterclause) > 1 )) begin select @notforrepl_bit = 0 end else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick or join_nickname = @tablenick) begin select @notforrepl_bit = 0 end else begin select @notforrepl_bit = 1 end select @notforrepl_str = ' if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1 -- and master.dbo.fn_isreplmergeagent() = 1 return ' /* Drop the article's replication triggers if they preexist */ exec dbo.sp_MSdroparticletriggers @source_table, @table_owner if @@ERROR <> 0 return 1 -- owner name removed set @instrigname = @ext + @guidstr set @deltrigname = @ext2 + @guidstr set @updtrigname = 'upd_' + @guidstr set @viewname = 'ctsv_' + @guidstr set @tsview = 'tsvw_' + @guidstr /* Make sure trigger name is unique */ exec @retcode=sp_MSuniqueobjectname @instrigname, @instrigname output if @retcode<>0 or @@ERROR<>0 return (1) exec @retcode=sp_MSuniqueobjectname @deltrigname, @deltrigname output if @retcode<>0 or @@ERROR<>0 return (1) exec @retcode=sp_MSuniqueobjectname @updtrigname, @updtrigname output if @retcode<>0 or @@ERROR<>0 return (1) /* Create the view if it doesn't already exist. */ if not exists (select * from sysobjects where type = 'V' and name = @viewname) begin exec @retcode=sp_MSuniqueobjectname @viewname, @viewname output if @retcode<>0 or @@ERROR<>0 return (1) set @command = 'create view dbo.' + @viewname + ' as select * from dbo.MSmerge_contents where trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@instrigname) + ''')) > 0 or trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@updtrigname) + ''')) > 0 or trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@deltrigname) + ''')) > 0 with check option' execute (@command) if @@ERROR<>0 return (1) set @command = 'grant update, insert, select, delete on ' + @viewname + ' to public' execute (@command) if @@ERROR<>0 return (1) execute ('sp_MS_marksystemobject ''' + @viewname + '''') if @@ERROR<>0 return (1) end /* Create the view if it doesn't already exist. */ if not exists (select * from sysobjects where type = 'V' and name = @tsview) begin exec @retcode=sp_MSuniqueobjectname @tsview, @tsview output if @retcode<>0 or @@ERROR<>0 return (1) set @command = 'create view dbo. ' + @tsview + ' as select * from dbo.MSmerge_tombstone where trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@instrigname) + ''')) > 0 or trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@updtrigname) + ''')) > 0 or trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.' + QUOTENAME(@deltrigname) + ''')) > 0 with check option' execute (@command) if @@ERROR<>0 return (1) set @command = 'grant update, insert, select, delete on ' + @tsview + ' to public' execute (@command) if @@ERROR<>0 return (1) execute ('sp_MS_marksystemobject ''' + @tsview + '''') if @@ERROR<>0 return (1) end /* If column tracking is on, construct the string to initialize colv's */ if (@column_tracking <> 0) select @ifcoltracking = ' set @colv1 = { fn INITCOLVS(@ccols, @nickname) }' else select @ifcoltracking = ' set @colv1 = NULL' /* UNDONE maybe remove null guid checks in SQL SERVER 7.0 */ select @command = 'create trigger ' + @instrigname + ' on ' + @source_table + ' for insert as ' + @notforrepl_str + ' /* Declare variables */ declare @article_rows_inserted int select @article_rows_inserted = count(*) from inserted declare @tablenick int, @nickname int declare @lineage varbinary(255), @colv1 varbinary(2048) declare @ccols int, @retcode smallint, @version int, @curversion int, @oldmaxversion int set nocount on set @tablenick = ' + @tablenickchar + ' select @ccols = ' + @ccolchar + ' set @lineage = 0x0 set @retcode = 0 select @oldmaxversion= maxversion_at_cleanup from sysmergearticles where nickname = @tablenick execute dbo.sp_MSgetreplnick @nickname = @nickname output if (@@error <> 0) begin goto FAILURE end set @lineage = { fn UPDATELINEAGE (0x0, @nickname, 1) } ' + @ifcoltracking + ' if (@@error <> 0) begin goto FAILURE end if exists (select ts.rowguid from ' + @tsview + ' ts, inserted i where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol) begin select @version = max({fn GETMAXVERSION(lineage)}) from ' + @tsview + ' where tablenick = @tablenick and rowguid in (select rowguidcol from inserted) if @version is not null begin -- reset lineage and colv to higher version... set @curversion = 0 while (@curversion <= @version) begin set @lineage = { fn UPDATELINEAGE (@lineage, @nickname, @oldmaxversion+1) } set @curversion = @curversion + 1 end if (@colv1 IS NOT NULL) set @colv1 = { fn UPDATECOLVBM(@colv1, @nickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) } delete from ' + @tsview + ' where tablenick = @tablenick and rowguid in (select rowguidcol from inserted) end end if (@article_rows_inserted = 1) begin if not exists (select ct.rowguid from ' + @viewname + ' ct, inserted i where ct.tablenick = @tablenick and ct.rowguid = i.rowguidcol) begin insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, joinchangegen) select @tablenick, rowguidcol, @lineage, @colv1, A.gen_cur, A.gen_cur from inserted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as A end end else begin insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, joinchangegen) select @tablenick, rowguidcol, @lineage, @colv1, A.gen_cur, A.gen_cur from inserted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as A where rowguidcol not in (select rowguid from ' + @viewname + ' where tablenick = @tablenick) end if @@error <> 0 goto FAILURE return FAILURE: if @@trancount > 0 rollback tran raiserror (20041, 16, -1) return ' execute (@command) if @@ERROR <> 0 begin raiserror(21304, 16, -1, @source_table) return (1) end select @command3 = 'sp_MS_marksystemobject ''' + REPLACE(@owner, '''', '''''') + '.' + @instrigname + '''' if @owner_is_admin = 1 execute (@command3) /* Call separate routine to add update trigger */ exec @retcode=sp_MSaddupdatetrigger @source_table, @owner, @object, @artid, @column_tracking, @viewname if @retcode<>0 or @@ERROR<>0 return (1) /* Now make the delete trigger */ -- NOTE: owner name removed /* Make sure trigger name is unique */ exec @retcode = dbo.sp_MSuniqueobjectname @deltrigname, @deltrigname output if @retcode<>0 or @@ERROR<>0 return (1) set @command = 'create trigger ' + @deltrigname + ' on ' + @source_table + ' FOR DELETE AS ' if (@notforrepl_bit = 1) select @command = @command + @notforrepl_str select @command = @command + ' /* Declare variables */ declare @tablenick int, @retcode smallint, @reason nvarchar(255), @nickname int, @lineage varbinary(255), @oldmaxversion int set nocount on select @tablenick = ' + @tablenickchar + ' select @oldmaxversion= maxversion_at_cleanup from sysmergearticles where nickname = @tablenick select @reason = ''user delete'' execute dbo.sp_MSgetreplnick @nickname = @nickname output if (@@error <> 0) begin goto FAILURE end ' set @command2 = ' set @lineage = { fn UPDATELINEAGE(0x0, @nickname, @oldmaxversion) } insert into ' + @tsview + ' (rowguid, tablenick, type, lineage, generation, reason) select rowguidcol, @tablenick, 1, { fn UPDATELINEAGE(COALESCE(c.lineage, @lineage), @nickname, @oldmaxversion+1) }, a.gen_cur, @reason from deleted d left outer join ' + @viewname + ' c on c.tablenick = @tablenick and c.rowguid = d.rowguidcol join (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as a on a.nickname = @tablenick if @@error <> 0 GOTO FAILURE delete from ' + @viewname + ' where tablenick = @tablenick and rowguid in (select rowguidcol from deleted) if @@error <> 0 GOTO FAILURE return FAILURE: if @@trancount > 0 rollback tran raiserror (20041, 16, -1) return ' execute (@command + @inscommand + @command2) if @@ERROR <> 0 begin raiserror(21304, 16, -1, @source_table) return (1) end select @command3 = 'sp_MS_marksystemobject ''' + REPLACE(@owner, '''', '''''') + '.' + @deltrigname + '''' if @owner_is_admin = 1 execute (@command3) return (0) GO exec dbo.sp_MS_marksystemobject sp_MSaddmergetriggers go grant exec on dbo.sp_MSaddmergetriggers to public go raiserror('Creating procedure sp_MShelpdestowner', 0,1) GO create procedure sp_MSchangeobjectowner( @tablename sysname, @dest_owner sysname )AS declare @uid smallint declare @retcode int select @uid=uid from sysobjects where name=@tablename --do not bother to call sp_changeobjectowner the object is owned by the @dest_owner itself if user_name(@uid)=@dest_owner return(0) exec @retcode=sp_changeobjectowner @tablename, @dest_owner if @@ERROR<>0 or @retcode<>0 begin raiserror(21346, 16, -1, @tablename, @dest_owner) return (1) end return(0) go exec dbo.sp_MS_marksystemobject sp_MSchangeobjectowner go grant exec on dbo.sp_MSchangeobjectowner to public go create procedure sp_MShelpdestowner( @spname sysname ) AS declare @comment nvarchar(400) declare @patindex int declare @has_destowner int select @comment = convert(nvarchar(400), text) from syscomments where id = object_id(@spname) select @patindex = PATINDEX('%@destowner%', @comment) if @patindex<>0 select 1 else select 0 GO exec dbo.sp_MS_marksystemobject sp_MShelpdestowner go grant exec on dbo.sp_MShelpdestowner to public go raiserror('Creating procedure sp_MSfillupmissingcols', 0,1) GO create procedure sp_MSfillupmissingcols(@publication sysname, @source_table sysname) AS declare @sync_objid int declare @columns varbinary(125) declare @missingcolid int declare @missingbm varbinary(125) declare @excludedcolid int declare @excludedbm varbinary(125) declare @excludedcolcnt int declare @missingcolcnt int declare @maxcolid int declare @column_tracking bit declare @id int declare @pubid uniqueidentifier declare @missingindex int select @id = object_id(@source_table) select @missingcolcnt = 0 select @excludedcolcnt = 0 select @pubid=pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name() select @columns = columns, @sync_objid=sync_objid from sysmergearticles where objid = @id and pubid=@pubid select @missingbm = 0x00 select @excludedbm = 0x00 select @missingcolid = 1 --instead of using the minimal colid in syscolumns with the same id, as we used to do select TOP 1 @maxcolid = colid from syscolumns where id=@id order by colid DESC while (@missingcolid <= @maxcolid) begin if exists (select * from syscolumns where colid = @missingcolid and id = @id and name not in (select name from syscolumns where id = @sync_objid)) begin select @excludedcolcnt = @excludedcolcnt + 1 select @missingindex = count(*) from syscolumns where id=@id and colid<=@missingcolid exec dbo.sp_MSsetbit @excludedbm OUTPUT, @missingindex end set @missingcolid = @missingcolid + 1 end UPDATE sysmergearticles set excluded_col_count = @excludedcolcnt, excluded_cols = @excludedbm where objid = @id and pubid=@pubid GO exec dbo.sp_MS_marksystemobject sp_MSfillupmissingcols go grant exec on dbo.sp_MSfillupmissingcols to public go raiserror('Creating procedure sp_MSmaptype', 0,1) GO create procedure sp_MSmaptype (@type nvarchar(60) output, @len smallint, @prec int, @scale int) as declare @typeout nvarchar(60) select @typeout = case @type when 'binary' then 'varbinary' when 'char' then 'varchar' when 'nchar' then 'nvarchar' when 'datetimn' then 'datetime' when 'decimaln' then 'decimal' when 'floatn' then 'float' when 'intn' then 'int' when 'moneyn' then 'money' when 'numericn' then 'numeric' when 'timestamp' then 'varbinary' when 'bit' then 'tinyint' else @type --for user defined data type which may contain space in between END -- append length or scale and precision if needed if (@typeout = 'varbinary' or @typeout = 'varchar' or @typeout = 'nvarchar') begin select @type = @typeout + '(' + rtrim(convert(nchar, @len)) + ')' return end if (@typeout = 'numeric' or @typeout = 'decimal') begin select @type = @typeout + '(' + rtrim(convert(nchar, @prec)) + ',' + rtrim(convert(nchar, @scale)) + ')' return end select @type = @typeout go exec dbo.sp_MS_marksystemobject sp_MSmaptype go raiserror('Creating procedure sp_MSquerysubtype', 0,1) GO create procedure sp_MSquerysubtype (@pubid uniqueidentifier,@subscriber sysname, @subscriber_db sysname) as declare @subtype int declare @srvid int SELECT @srvid = srvid FROM master..sysservers WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default select @subtype = NULL select @subtype = subscription_type from sysmergesubscriptions where pubid=@pubid and db_name=@subscriber_db and srvid=@srvid if @subtype is NULL select @subtype=2 select @subtype go exec dbo.sp_MS_marksystemobject sp_MSquerysubtype go grant exec on dbo.sp_MSquerysubtype to public go -- sp_showrowreplicainfo displays row and column version information about a specific row -- parameters: -- @ownername: The table owner. -- @tablename: Name of the table that contains the row. -- @rowguid: Unique identifier of the row of interest. -- @show: Determines whether to display row versions, colum versions, or both. Values are 'both', 'row', or 'columns'. -- output columns for row version information: -- server_name: Name of the server that made this entry -- db_name: Name of the database that made this entry. -- db_nickname: Nickname of the database that made this entry. -- version: Version of the entry. -- rowversion_table: Indicates whether lineage is in MSmerge_contents or MSmerge_tombstone. -- comment: Records problems with this version entry. -- output columns for column version information: -- server_name, db_name, db_nickname, version, comment: As above. -- colname: Name of the column the colv entry stands for. create procedure sp_showrowreplicainfo (@ownername sysname = NULL, @tablename sysname = NULL, @rowguid uniqueidentifier, @show nvarchar(20) = 'both') as set nocount on -- some constants -- this stored proc is for debugging purposes, thus no need for localizing them declare @dbname sysname select @dbname= db_name() declare @missingcolname sysname set @missingcolname= '<Missing column>' declare @anonymousname sysname set @anonymousname= '<Anonymous subscriber>' declare @unknownname sysname set @unknownname= '<Unknown server name>' declare @mergename sysname set @mergename= '<Merge nickname>' declare @nondecreasingversion nvarchar(128) set @nondecreasingversion= 'Problem found: Non-decreasing version.' declare @toohighcolvversion nvarchar(128) set @toohighcolvversion= 'Problem found: Version is higher than highest version in lineage.' declare @naname sysname set @naname= '<n/a>' if @tablename is NULL begin set @tablename= (select top 1 object_name(objid) from sysmergearticles where nickname = (select tablenick from MSmerge_contents where rowguid = @rowguid)) end if @tablename is NULL begin set @tablename= (select top 1 object_name(objid) from sysmergearticles where nickname = (select tablenick from MSmerge_tombstone where rowguid = @rowguid)) end -- check whether given table exists if not exists (select * from sysobjects where xtype = 'U' and name = @tablename) begin raiserror (20507, 16, 1, @tablename, @dbname) return 1 end else if @ownername is not null begin -- check whether table belongs to the given owner if not exists (select * from sysobjects where xtype = 'U' and name=@tablename and uid = (select uid from sysusers where name = @ownername)) begin declare @qualifiedtablename nvarchar(257) select @qualifiedtablename= @ownername + '.' + @dbname raiserror (20507, 16, 1, @qualifiedtablename, @dbname) return 1 end end -- get tableid and tablenick from tablename declare @tableid int declare @tablenick int set @tableid= object_id(@tablename) select @tablenick = (select top 1 nickname from sysmergearticles where objid = @tableid) if @tablenick is null begin raiserror (20027, 16, 1, @tablename) return 1 end -- check whether there is an entry for this row in either MSmerge_contents or MSmerge_tombstone declare @incontents int declare @lineage varbinary(249) select @lineage= lineage from MSmerge_contents where rowguid = @rowguid if @lineage is not null begin set @incontents= 1 end else begin select @lineage= lineage from MSmerge_tombstone where rowguid = @rowguid if @lineage is not null begin set @incontents= 0 end else begin raiserror(21511,10,1) return 0 end end -- create temporary table for information about lineage and colv entries create table #results (type nchar(7) null, rowversion_table nchar(17) null, server_name sysname null, [db_name] sysname null, db_nickname int not null, colid int null, colname sysname null, version int not null, comment nvarchar(255) null, id int identity(1,1) not null) if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'row') begin -- insert lineage information into temptable insert into #results (db_nickname, version) exec master..xp_showlineage @lineage update #results set type= 'lineage' if (@incontents = 1) begin update #results set rowversion_table = 'MSmerge_contents' end else begin update #results set rowversion_table = 'MSmerge_tombstone' end end if (@incontents = 1) and (lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'columns')) begin -- insert colv information into temptable declare @colv varbinary(2048) select @colv= colv1 from MSmerge_contents where rowguid=@rowguid if @colv is not null begin insert into #results (colid, db_nickname, version) exec master..xp_showcolv @colv update #results set type= 'colv' where type is null -- translate colids into column names if (select top 1 missing_col_count from sysmergearticles where nickname = @tablenick) = 0 begin -- no missing cols: position of entries in colv correspond to colid in sysmergearticles update #results set colname= s.name from syscolumns s where #results.colid = s.colid and s.id = @tableid end else begin -- missing cols: colv has entries for columns that do not exist in this db declare @colname sysname declare @ismissing int declare @missingsofar int declare @colid int declare @missingcols varbinary(128) select @missingcols= (select top 1 missing_cols from sysmergearticles where nickname = @tablenick) set @missingsofar= 0 select @colid= (select min(colid) from #results where colname is null and colid is not null) while @colid is not null begin -- is this column missing? exec @ismissing= sp_MStestbit @missingcols, @colid if @ismissing <> 0 begin update #results set colname= @missingcolname, server_name= @naname, [db_name]= @naname where colid = @colid set @missingsofar= @missingsofar + 1 end else begin select @colname= (select name from syscolumns where id = @tableid and colid = (@colid - @missingsofar)) update #results set colname= @colname where colid = @colid end select @colid= (select min(colid) from #results where colname is null and colid is not null) end end end end -- transform null comment to empty strings update #results set comment= '' -- translate nicknames in temptable into real db names; set server names, too declare @subid uniqueidentifier declare @servername sysname declare @srvid int declare @replnick int update #results set [db_name]= @mergename, server_name= @naname where db_nickname=1 select @replnick= (select top 1 db_nickname from #results where [db_name] is null) while @replnick is not null begin select @subid= (select top 1 s.subid from sysmergesubscriptions s, MSmerge_replinfo r where r.repid = s.subid and @replnick = r.replnickname) select @dbname= (select [db_name] from sysmergesubscriptions where subid = @subid) if @dbname is null begin set @dbname=@anonymousname set @servername= @unknownname end else begin select @servername= (select srv.srvname from master..sysservers srv, sysmergesubscriptions sub where srv.srvid = sub.srvid and sub.subid = @subid) end update #results set [db_name]= @dbname, server_name= @servername where db_nickname = @replnick and [db_name] is null select @replnick= (select top 1 db_nickname from #results where [db_name] is null) end -- record increasing lineage versions update #results set comment= @nondecreasingversion where type = 'lineage' and exists (select * from #results r where r.version < #results.version and r.id < #results.id) -- record colv versions that are higher than highest lineage version update #results set comment= @toohighcolvversion where type = 'colv' and not exists (select version from #results r where type = 'lineage' and r.version >= #results.version) -- deliver results if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'row') begin select server_name, [db_name], db_nickname, version, rowversion_table, comment from #results where type = 'lineage' order by id end if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'columns') begin select server_name, db_name, db_nickname, version, colname, comment from #results where type = 'colv' order by id end drop table #results return 0 go exec dbo.sp_MS_marksystemobject sp_showrowreplicainfo go grant execute on dbo.sp_showrowreplicainfo to public go create procedure sp_MSsethighestversion (@artnick int) as set nocount on declare @minlineagelength int set @minlineagelength= 8 declare @maxversion int declare @maxversiontombstone int set @maxversion= (select max({fn GETMAXVERSION(mc.lineage)}) from MSmerge_contents as mc inner join #oldgens as og on (mc.generation = og.gen) where tablenick = @artnick and lineage is not null and datalength(lineage) >= @minlineagelength) if @@error<>0 goto Failure set @maxversiontombstone= (select max({fn GETMAXVERSION(mt.lineage)}) from MSmerge_tombstone as mt inner join #oldgens as og on (mt.generation = og.gen) where tablenick = @artnick and lineage is not null and datalength(lineage) >= @minlineagelength) if @@error<>0 goto Failure if @maxversion is null begin set @maxversion= @maxversiontombstone end if @maxversiontombstone is not null and @maxversiontombstone > @maxversion begin set @maxversion= @maxversiontombstone end if @maxversion is not null begin update sysmergearticles set maxversion_at_cleanup= @maxversion where nickname = @artnick and maxversion_at_cleanup < @maxversion if @@error<>0 goto Failure end return 0 Failure: return 1 go exec dbo.sp_MS_marksystemobject sp_MSsethighestversion go grant execute on dbo.sp_MSsethighestversion to public go create procedure sp_mergemetadataretentioncleanup (@num_genhistory_rows int = 0 output, @num_contents_rows int = 0 output, @num_tombstone_rows int = 0 output) as declare @maxretention int declare @artnick int declare @gen int declare @retcode smallint declare @bi_objid int declare @cmd nvarchar(200) declare @guidnull uniqueidentifier declare @delbatchsize int declare @delcount int declare @applockname nvarchar(255) set @num_genhistory_rows= 0 set @num_contents_rows= 0 set @num_tombstone_rows= 0 -- if somebody else is already cleaning up in this database, we simply return set @applockname= 'MS_sp_mergemetadataretentioncleanup' + convert(nvarchar(11), db_id()) exec @retcode= sp_getapplock @Resource= @applockname, @LockMode= 'Exclusive', @LockOwner= 'Session', @LockTimeout= 0 if @@error <> 0 or @retcode < 0 return (0) set @guidnull= '00000000-0000-0000-0000-000000000000' set @delbatchsize= 5000 create table #oldgens (gen int unique clustered) -- iterate over all articles that do not belong to a publication with infinite retention declare article_curs cursor local fast_forward for select distinct nickname from sysmergearticles where nickname not in (select distinct a.nickname from sysmergearticles as a inner join sysmergepublications as p on (a.pubid = p.pubid) where isnull(p.retention, 0) = 0) open article_curs fetch next from article_curs into @artnick while (@@fetch_status <> -1) begin -- find max retention of all pubs the article belongs to select @maxretention= max(isnull(retention,0)) from sysmergepublications where pubid in (select pubid from sysmergearticles where nickname = @artnick) -- add one to make up for maximal possible timezone differences, plus one to compensate for clock inaccuracies set @maxretention= @maxretention + 1 delete from #oldgens insert into #oldgens select distinct generation from MSmerge_genhistory where art_nick = @artnick and guidlocal <> @guidnull and coldate < dateadd(day, -@maxretention, getdate()) -- go to next article if this one has no stale generations if @@rowcount = 0 begin fetch next from article_curs into @artnick continue end -- set highest version in sysmergearticles exec @retcode= sp_MSsethighestversion @artnick= @artnick if @retcode<>0 or @@error<>0 goto Failure -- clean up contents, tombstone, before image (if it exists), genhistory set rowcount @delbatchsize set @delcount= @delbatchsize while @delcount = @delbatchsize begin delete mc from MSmerge_contents as mc inner join #oldgens as og on (mc.generation = og.gen) where mc.tablenick = @artnick set @delcount= @@rowcount set @num_contents_rows= @num_contents_rows + @delcount end set @delcount= @delbatchsize while @delcount = @delbatchsize begin delete mt from MSmerge_tombstone as mt inner join #oldgens as og on (mt.generation = og.gen) where tablenick = @artnick set @delcount= @@rowcount set @num_tombstone_rows= @num_tombstone_rows + @delcount end set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @artnick) if @bi_objid is not null begin set @cmd= 'delete bi from ' + object_name(@bi_objid) + ' as bi inner join #oldgens as og on (bi.generation = og.gen)' set @delcount= @delbatchsize while @delcount = @delbatchsize begin exec dbo.sp_executesql @cmd set @delcount= @@rowcount end end set @delcount= @delbatchsize while @delcount = @delbatchsize begin delete gh from MSmerge_genhistory as gh inner join #oldgens as og on (gh.generation = og.gen) where art_nick = @artnick set @delcount= @@rowcount set @num_genhistory_rows= @num_genhistory_rows + @delcount end set rowcount 0 -- get next article fetch next from article_curs into @artnick end close article_curs deallocate article_curs drop table #oldgens exec sp_MScleanup_zeroartnick_genhistory @num_genhistory_rows output, @num_contents_rows output exec @retcode= sp_releaseapplock @Resource= @applockname, @LockOwner= 'Session' if @@error <> 0 or @retcode < 0 return (1) else return (0) Failure: close article_curs deallocate article_curs drop table #oldgens exec sp_releaseapplock @Resource= @applockname, @LockOwner= 'Session' return (1) go exec dbo.sp_MS_marksystemobject sp_mergemetadataretentioncleanup go grant execute on dbo.sp_mergemetadataretentioncleanup to public go raiserror('Creating procedure sp_MSpurgecontentsorphans', 0,1) go create procedure sp_MSpurgecontentsorphans as declare @retcode smallint create table #oldgens (artnick int, gen int) create unique clustered index ucOldgens on #oldgens(artnick, gen) -- find generations that exist in MSmerge_contents but not in MSmerge_genhistory insert into #oldgens (artnick, gen) select distinct tablenick, generation from dbo.MSmerge_contents where generation not in (select distinct generation from dbo.MSmerge_genhistory) exec @retcode = sp_MSdelete_specifiedcontents drop table #oldgens return @retcode go exec dbo.sp_MS_marksystemobject sp_MSpurgecontentsorphans go grant execute on dbo.sp_MSpurgecontentsorphans to public go raiserror('Creating procedure sp_MScleanup_zeroartnick_genhistory', 0,1) go create procedure sp_MScleanup_zeroartnick_genhistory (@num_genhistory_rows int = 0 output, @num_contents_rows int = 0 output) as declare @retcode smallint declare @maxretention int declare @guidnull uniqueidentifier declare @oldgencount int declare @zeroartnickgencount int -- If there is any publication that has infinite retention, then we -- should not clean up genhistory rows that have 0 art_nick. This is -- because the gen could potentially have changes in articles that belong -- to that publication. if exists (select * from sysmergepublications where isnull(retention, 0) = 0) return 0 -- Now we know we only have publications that have a finite retention period. -- Let us choose the highest retention period across all publications and use -- that when cleaning up generations with 0 art_nick. Again this is because this -- gen could have changes in articles from any of those publications. It is safer -- to be pessimistic. select @maxretention = max(isnull(retention,0)) from sysmergepublications -- add one to make up for maximal possible timezone differences, plus one to compensate for clock inaccuracies set @maxretention= @maxretention + 1 create table #oldgens (artnick int, gen int) create unique clustered index ucOldgens on #oldgens(artnick, gen) create table #zeroartnickgens (gen int) create unique clustered index ucZeroartnickgens on #zeroartnickgens(gen) set @guidnull= '00000000-0000-0000-0000-000000000000' insert into #zeroartnickgens (gen) select distinct generation from MSmerge_genhistory where art_nick = 0 and generation > 1 and guidlocal <> @guidnull and coldate < dateadd(day, -@maxretention, getdate()) select @zeroartnickgencount = @@rowcount if (@zeroartnickgencount = 0) begin drop table #oldgens drop table #zeroartnickgens return 0 end -- find entries that exist in MSmerge_contents that have art_nick = 0 in MSmerge_genhistory insert into #oldgens (artnick, gen) select distinct tablenick, generation from dbo.MSmerge_contents where generation in (select gen from #zeroartnickgens) select @oldgencount = @@rowcount set @retcode = 0 if (@oldgencount > 0) begin exec @retcode = sp_MSdelete_specifiedcontents @num_contents_rows output end if (@@error = 0 and @retcode = 0) begin declare @delcount int declare @delbatchsize int set @delbatchsize= 5000 set rowcount @delbatchsize set @delcount= @delbatchsize while @delcount = @delbatchsize begin delete gh from MSmerge_genhistory as gh inner join #zeroartnickgens as zag on (gh.generation = zag.gen) where art_nick = 0 and generation > 1 and guidlocal <> @guidnull and coldate < dateadd(day, -@maxretention, getdate()) set @delcount= @@rowcount set @num_genhistory_rows= @num_genhistory_rows + @delcount end set rowcount 0 end drop table #oldgens drop table #zeroartnickgens return @retcode go exec dbo.sp_MS_marksystemobject sp_MScleanup_zeroartnick_genhistory go raiserror('Creating procedure sp_MSdelete_specifiedcontents', 0,1) go create procedure sp_MSdelete_specifiedcontents (@num_contents_rows int = 0 output) as declare @retcode smallint -- iterate over articles in the temptable declare @artnick int declare article_curs cursor local fast_forward for select distinct artnick from #oldgens open article_curs fetch next from article_curs into @artnick while (@@fetch_status <> -1) begin -- if necessary, update highest version in sysmergearticles exec @retcode= sp_MSsethighestversion @artnick= @artnick if @retcode<>0 or @@error<>0 goto Failure -- clean up orphaned rows in MSmerge_contents declare @delcount int declare @delbatchsize int set @delbatchsize= 5000 set rowcount @delbatchsize set @delcount= @delbatchsize while @delcount = @delbatchsize begin delete mc from MSmerge_contents as mc inner join #oldgens as og on (mc.tablenick = og.artnick and mc.generation = og.gen) where mc.tablenick = @artnick set @delcount= @@rowcount set @num_contents_rows = @num_contents_rows + @delcount end -- get next article fetch next from article_curs into @artnick end set rowcount 0 close article_curs deallocate article_curs return(0) Failure: close article_curs deallocate article_curs return (1) go exec dbo.sp_MS_marksystemobject sp_MSdelete_specifiedcontents go dump tran master with no_log go checkpoint go set nocount on go execute dbo.sp_configure 'update',1 go reconfigure with override go set ANSI_NULLS off go dump tran master with no_log go use master go /* ** Drop the stored procedures in this script using the old dropping SP ** and then drop itself */ if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_rladmin') begin drop procedure sp_MSdrop_rladmin end /* ** Create stored procedures to drop the stored procedures ** created by this script */ raiserror('Creating procedure sp_MSdrop_rladmin', 0,1) GO create procedure sp_MSdrop_rladmin as if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSaddmergepub_snapshot') drop procedure sp_MSaddmergepub_snapshot if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSremove_userscript') drop procedure sp_MSremove_userscript if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSretrieve_mergepublication') drop procedure sp_MSretrieve_mergepublication if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSadjustmergeidentity') drop procedure sp_MSadjustmergeidentity if exists (select * from sysobjects where type in ('P ') and name = 'sp_MScheckatpublisher') drop procedure sp_MScheckatpublisher if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSdropmergepub_snapshot') drop procedure sp_MSdropmergepub_snapshot if exists (select * from sysobjects where type = 'P' and name = 'sp_addmergepublication') drop procedure sp_addmergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_changemergepublication') drop procedure sp_changemergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_MSCheckmergereplication') drop procedure sp_MSCheckmergereplication if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergepublication') drop procedure sp_helpmergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_mergearticlecolumn') drop procedure sp_mergearticlecolumn if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergearticlecolumn') drop procedure sp_helpmergearticlecolumn if exists (select * from sysobjects where type = 'P' and name = 'sp_MSpublicationview') drop procedure sp_MSpublicationview if exists (select * from sysobjects where type = 'P' and name = 'sp_reinitmergesubscription') drop procedure sp_reinitmergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_MSreinitmergepublication') drop procedure sp_MSreinitmergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_MScleanup_conflict_table') drop procedure sp_MScleanup_conflict_table if exists (select * from sysobjects where type = 'P' and name = 'sp_MScleanup_conflict') drop procedure sp_MScleanup_conflict if exists (select * from sysobjects where type = 'P' and name = 'sp_MScleanup_metadata') drop procedure sp_MScleanup_metadata if exists (select * from sysobjects where type = 'P' and name = 'sp_mergecleanupmetadata') drop procedure sp_mergecleanupmetadata if exists (select * from sysobjects where type = 'P' and name = 'sp_MSquiescetriggerson') drop procedure sp_MSquiescetriggerson if exists (select * from sysobjects where type = 'P' and name = 'sp_MSquiescecheck') drop procedure sp_MSquiescecheck if exists (select * from sysobjects where type = 'P' and name = 'sp_MSquiescetriggersoff') drop procedure sp_MSquiescetriggersoff if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergecleanupwait') drop procedure sp_helpmergecleanupwait if exists (select * from sysobjects where type = 'P' and name = 'sp_mergepreparecleanup') drop procedure sp_mergepreparecleanup if exists (select * from sysobjects where type = 'P' and name = 'sp_MSquiesceforcleanup') drop procedure sp_MSquiesceforcleanup if exists (select * from sysobjects where type = 'P' and name = 'sp_mergecompletecleanup') drop procedure sp_mergecompletecleanup if exists (select * from sysobjects where type = 'P' and name = 'sp_MScompletecleanup') drop procedure sp_MScompletecleanup if exists (select * from sysobjects where type = 'P' and name = 'sp_MSpreparecleanup') drop procedure sp_MSpreparecleanup if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdrop_expired_mergesubscription') drop procedure sp_MSdrop_expired_mergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_dropmergepublication') drop procedure sp_dropmergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddmergeschemaarticle') drop procedure sp_MSaddmergeschemaarticle if exists (select * from sysobjects where type = 'P' and name = 'sp_addmergearticle') drop procedure sp_addmergearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSchangemergeschemaarticle') drop procedure sp_MSchangemergeschemaarticle if exists (select * from sysobjects where type = 'P' and name = 'sp_changemergearticle') drop procedure sp_changemergearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSreinit_hub') drop procedure sp_MSreinit_hub if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergearticle') drop procedure sp_helpmergearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_dropmergearticle') drop procedure sp_dropmergearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_addmergesubscription') drop procedure sp_addmergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_changemergesubscription') drop procedure sp_changemergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergesubscription') drop procedure sp_helpmergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_dropmergesubscription') drop procedure sp_dropmergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergefilter') drop procedure sp_helpmergefilter if exists (select * from sysobjects where type = 'P' and name = 'sp_changemergefilter') drop procedure sp_changemergefilter if exists (select * from sysobjects where type = 'P' and name = 'sp_addmergefilter') drop procedure sp_addmergefilter if exists (select * from sysobjects where type = 'P' and name = 'sp_dropmergefilter') drop procedure sp_dropmergefilter if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSmergepublishdb') drop procedure sp_MSmergepublishdb if exists (select * from sysobjects where type in ('P ') and name = 'sp_helpallowmerge_publication') drop procedure sp_helpallowmerge_publication if exists (select * from sysobjects where type in ('P ') and name = 'sp_enumcustomresolvers') drop procedure sp_enumcustomresolvers if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSenumpubreferences') drop procedure sp_MSenumpubreferences if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSscript_dri') drop procedure sp_MSscript_dri if exists (select * from sysobjects where type in ('P ') and name = 'sp_MSsubsetpublication') drop procedure sp_MSsubsetpublication if exists (select * from sysobjects where type = 'P' and name = 'sp_generatefilters') drop procedure sp_generatefilters if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakejoinfilter') drop procedure sp_MSmakejoinfilter if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeexpandproc') drop procedure sp_MSmakeexpandproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSindexcolfrombin') drop procedure sp_MSindexcolfrombin if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpvalidationdate') drop procedure sp_MShelpvalidationdate if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergearticleconflicts') drop procedure sp_helpmergearticleconflicts if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergeconflictpublications') drop procedure sp_MShelpmergeconflictpublications if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergeconflictcounts') drop procedure sp_MShelpmergeconflictcounts if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergeconflictrows') drop procedure sp_helpmergeconflictrows if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergedeleteconflictrows') drop procedure sp_helpmergedeleteconflictrows if exists (select * from sysobjects where type = 'P' and name = 'sp_deletemergeconflictrow') drop procedure sp_deletemergeconflictrow if exists (select * from sysobjects where type = 'P' and name = 'sp_getmergedeletetype') drop procedure sp_getmergedeletetype if exists (select * from sysobjects where type = 'P' and name = 'sp_mergedummyupdate') drop procedure sp_mergedummyupdate if exists (select * from sysobjects where type = 'P' and name = 'sp_validatemergepublication') drop procedure sp_validatemergepublication if exists (select * from sysobjects where type = 'P' and name = 'sp_validatemergesubscription') drop procedure sp_validatemergesubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_addtabletocontents') drop procedure sp_addtabletocontents if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddpubtocontents') drop procedure sp_MSaddpubtocontents if exists (select * from sysobjects where type = 'P' and name = 'sp_MSget_subtypedatasrc') drop procedure sp_MSget_subtypedatasrc if exists (select * from sysobjects where type = 'P' and name = 'sp_addmergealternatepublisher') drop procedure sp_addmergealternatepublisher if exists (select * from sysobjects where type = 'P' and name = 'sp_helpmergealternatepublisher') drop procedure sp_helpmergealternatepublisher if exists (select * from sysobjects where type = 'P' and name = 'sp_dropmergealternatepublisher') drop procedure sp_dropmergealternatepublisher if exists (select * from sysobjects where type = 'P' and name = 'sp_MScomputemergearticlescreationorder') drop procedure sp_MScomputemergearticlescreationorder if exists (select * from sysobjects where type = 'P' and name = 'sp_MSclearcolumnbit') drop procedure sp_MSclearcolumnbit if exists (select * from sysobjects where type = 'P' and name = 'sp_MScomputemergeunresolvedrefs') drop procedure sp_MScomputemergeunresolvedrefs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetpubinfo') drop procedure sp_MSgetpubinfo if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddmergedynamicsnapshotjob') drop procedure sp_MSaddmergedynamicsnapshotjob if exists (select * from sysobjects where type = 'P' and name = 'sp_MSpropagateschematorepubs') drop procedure sp_MSpropagateschematorepubs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdropmergedynamicsnapshotjob') drop procedure sp_MSdropmergedynamicsnapshotjob if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergedynamicsnapshotjob') drop procedure sp_MShelpmergedynamicsnapshotjob go exec dbo.sp_MS_marksystemobject sp_MSdrop_rladmin go EXEC dbo.sp_MSdrop_rladmin GO raiserror('Creating procedure sp_MSaddmergepub_snapshot', 0,1) GO CREATE PROCEDURE sp_MSaddmergepub_snapshot ( @publication sysname, @freqtype int = 4 , /* 4== Daily */ @freqinterval int = 1, /* Every day */ @freqsubtype int = 4, /* Sub interval = Minute */ @freqsubinterval int = 5, /* Every five minutes */ @freqrelativeinterval int = 1, @freqrecurrencefactor int = 0, @activestartdate int = 0, /* 12:00 am - 11:59 pm */ @activeenddate int = 99991231 , /* No start date */ @activestarttimeofday int = 0, @activeendtimeofday int = 235959, /* No end time */ @newtaskid int = 0 OUTPUT, @snapshot_job_name nvarchar(100) = null ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @distributor sysname declare @dist_rpcname sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @database sysname declare @newid int declare @fFoundPublication int declare @task_args nvarchar(4000) declare @pubid uniqueidentifier declare @snapshot_jobid binary(16) declare @job_existing bit /* ** Initializations */ select @fFoundPublication = 0 if (@snapshot_job_name is null) or (@snapshot_job_name = N'') begin select @job_existing = 0 end else begin select @job_existing = 1 end EXEC @retcode = dbo.sp_helpmergepublication @publication, @fFoundPublication output, @pubid output if @@ERROR <> 0 OR @retcode <> 0 BEGIN RETURN (1) END /* If the publication does not exist return error */ if @fFoundPublication = 0 BEGIN RAISERROR (21040, 11, -1, @publication) RETURN (1) END /* ** Make sure the publication does not already have a task. */ if EXISTS (select * FROM MSmerge_replinfo WHERE repid = @pubid and snapshot_jobid IS NOT NULL) BEGIN RAISERROR (14101, 11, -1, @publication) RETURN(1) END /* ** Get distributor information */ EXEC @retcode = dbo.sp_helpdistributor @distributor = @distributor OUTPUT, @distribdb = @distribdb OUTPUT, @rpcsrvname = @dist_rpcname OUTPUT if @@error <> 0 OR @retcode <> 0 or @distributor IS NULL OR @distribdb IS NULL BEGIN RAISERROR (14071, 16, -1) RETURN (1) END select @database = DB_NAME() select @task_args = '-Publisher ' + QUOTENAME(@@SERVERNAME) select @task_args = @task_args + ' -PublisherDB ' + QUOTENAME(@database) select @task_args = @task_args + ' -Distributor ' + QUOTENAME(@distributor) select @task_args = @task_args + ' -Publication ' + QUOTENAME(@publication) select @task_args = @task_args + ' -ReplicationType 2' /* ** Create task on distributor */ SELECT @distproc = RTRIM(@dist_rpcname) + '.' + @distribdb + '.dbo.sp_MSadd_snapshot_agent' EXECUTE @retcode = @distproc @name = @snapshot_job_name, @publisher = @@SERVERNAME, @publisher_db = @database, @publication = @publication, @publication_type = 2, -- Merge type @local_job = 1, @freqtype = @freqtype, @freqinterval = @freqinterval, @freqsubtype = @freqsubtype, @freqsubinterval = @freqsubinterval, @freqrelativeinterval = @freqrelativeinterval, @freqrecurrencefactor = @freqrecurrencefactor, @activestartdate = @activestartdate, @activeenddate =@activeenddate , @activestarttimeofday = @activestarttimeofday, @activeendtimeofday = @activeendtimeofday, @command = @task_args, @snapshot_jobid = @snapshot_jobid OUTPUT, @job_existing = @job_existing if @@ERROR <> 0 or @retcode <> 0 RETURN(1) SELECT @newtaskid = 1 UPDATE MSmerge_replinfo set snapshot_jobid = @snapshot_jobid WHERE repid = @pubid if @@ERROR <> 0 RETURN(1) return (0) GO exec dbo.sp_MS_marksystemobject sp_MSaddmergepub_snapshot go grant execute on dbo.sp_MSaddmergepub_snapshot to public go GO raiserror('Creating procedure sp_MSdropmergepub_snapshot', 0,1) GO CREATE PROCEDURE sp_MSdropmergepub_snapshot ( @publication sysname, @ignore_distributor bit = 0 ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @distributor sysname declare @distproc nvarchar(300) declare @snapshot_jobid binary(16) declare @fFoundPublication int declare @pubid uniqueidentifier declare @distribdb sysname /* ** Initializations */ select @fFoundPublication = 0 /* validate the publication */ /* If the publication is not exist found return error */ EXEC @retcode = dbo.sp_helpmergepublication @publication, @fFoundPublication output, @pubid output if @@ERROR <> 0 OR @fFoundPublication = 0 OR @retcode <> 0 BEGIN RETURN (1) END /* ** Get taskid. Make sure the snapshot_jobid is not NULL only before using MAX ** Otherwise there will be a warnning. */ select @snapshot_jobid = max(snapshot_jobid) FROM MSmerge_replinfo WHERE repid = @pubid if (@snapshot_jobid IS NOT NULL) begin /* ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC. */ if @ignore_distributor = 0 begin /* ** Get distributor information */ EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT if @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14071, 16, -1) RETURN (1) END /* ** Delete sync agent of Publication. */ declare @dbname sysname set @dbname = DB_NAME() SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSdrop_snapshot_agent' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @dbname, @publication = @publication IF @@ERROR <> 0 or @retcode <> 0 RETURN(1) end /* Update publication's taskid */ UPDATE MSmerge_replinfo set snapshot_jobid = NULL WHERE repid = @pubid IF @@ERROR <> 0 BEGIN RAISERROR (20072, 16, -1) RETURN (1) END end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSdropmergepub_snapshot go grant execute on dbo.sp_MSdropmergepub_snapshot to public go raiserror('Creating procedure sp_MScheckatpublisher', 0,1) GO create procedure sp_MScheckatpublisher @pubid uniqueidentifier AS -- this routine will be called by sp_addmergearticle, sp_changemergearticle declare @db_name sysname select @db_name =db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> db_name() RETURN 1 ELSE RETURN 0 go exec dbo.sp_MS_marksystemobject sp_MScheckatpublisher go grant execute on dbo.sp_MScheckatpublisher to public go raiserror('Creating procedure sp_MSaddmergeschemaarticle', 0,1) GO create procedure sp_MSaddmergeschemaarticle @pubid uniqueidentifier, @article sysname, @source_object sysname, @type tinyint, @description nvarchar(255), @status nvarchar(10), @pre_creation_command int, @creation_script nvarchar(255), @source_owner sysname, @destination_owner sysname, @schema_option binary(8), @destination_object sysname, @qualified_name nvarchar(270), @publication sysname, @snapshot_ready int, @force_invalidate_snapshot bit AS SET NOCOUNT ON DECLARE @retcode int DECLARE @objid int DECLARE @artid uniqueidentifier DECLARE @bInTran bit DECLARE @valid_schema_options int DECLARE @bump_to_80 bit SELECT @bInTran = 0 SELECT @objid = OBJECT_ID(@qualified_name) -- We are going to bump the compatibility level anyway -- since we are adding schema only articles SELECT @bump_to_80 = 1 RAISERROR(21391, 10, -1, @publication) /* ** Parameter check: Source object type must match ** specified article type. ** */ -- Type has to be one of 0x20, 0x40, or 0x80 by the time -- this sp is called IF @type = 0x20 BEGIN IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = @objid AND xtype = 'P ') BEGIN RAISERROR(21219, 16, -1) RETURN (1) END END ELSE IF @type = 0x40 BEGIN IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = @objid AND xtype = 'V ') BEGIN RAISERROR(21221, 16, -1) RETURN (1) END END ELSE IF @type = 0x80 BEGIN IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = @objid AND xtype IN ('FN','TF', 'IF')) BEGIN RAISERROR(21228, 16, -1) RETURN (1) END END /* ** Parameter check: @schema_option ** For proc and func schema only articles, schema_option can ** only contain the options ** 0x0000000000000001, 0x0000000000002000 ** for schema only articles except view. View articles can contain ** the options 0x0000000000000010, 0x0000000000000020, and 0x0000000000000100 ** in addition to the aforementioned options. */ IF @type = 0x40 BEGIN -- Since only the lower 32 bits of @schema_option are -- currently used, the following check is sufficient. -- Note that @schema_option should have been padded out already. DECLARE @schema_option_lodword int SELECT @valid_schema_options = 0x2151 SELECT @schema_option_lodword = fn_replgetbinary8lodword(@schema_option) IF (@schema_option_lodword & ~@valid_schema_options) <> 0 BEGIN RAISERROR (21229, 16, -1) RETURN (1) END END ELSE IF @schema_option NOT IN (0x0000000000000000, 0x0000000000000001, 0x0000000000002000, 0x0000000000002001) BEGIN RAISERROR (21222, 16, -1) RETURN (1) END /* ** Parameter check: @pre_creation_command must be ** 'drop' (id = 1) or 'none' (id = 0) */ IF @pre_creation_command NOT IN (0, 1) BEGIN RAISERROR(21223, 16, -1) RETURN (1) END -- Reuse @artid from articles publishing the same object so we can -- link these articles together during the reinit/republishing scenario SELECT @artid = NULL SELECT @artid = artid FROM sysmergeschemaarticles WHERE objid = @objid IF @artid IS NULL BEGIN SELECT @artid = newid() END BEGIN TRAN SAVE TRANSACTION sp_MSaddmergeschemaarticle SELECT @bInTran = 1 IF @snapshot_ready > 0 BEGIN IF @force_invalidate_snapshot = 0 BEGIN RAISERROR(21364, 16, -1, @article) GOTO FAILURE END RAISERROR(21360, 10, -1, @publication) SELECT @bump_to_80 = 1 UPDATE sysmergepublications SET snapshot_ready=2 WHERE pubid=@pubid IF @@ERROR<>0 GOTO FAILURE END INSERT sysmergeschemaarticles (name, type, objid, artid, description, pre_creation_command, pubid, status, creation_script, schema_option, destination_object, destination_owner) VALUES (@article, @type, @objid, @artid, @description, @pre_creation_command, @pubid, 1, @creation_script, @schema_option, @destination_object, @destination_owner) IF @@ERROR <> 0 GOTO FAILURE -- Mark the source object as a replication object so -- user cannot drop it UPDATE sysobjects SET replinfo = replinfo | 0x00000200 WHERE id = @objid IF @@ERROR <> 0 GOTO FAILURE IF @bump_to_80=1 BEGIN EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40 IF @@ERROR<>0 or @retcode<>0 GOTO FAILURE END COMMIT TRANSACTION RETURN (0) FAILURE: IF @bInTran = 1 BEGIN ROLLBACK TRANSACTION sp_MSaddmergeschemaarticle COMMIT TRANSACTION END RETURN (1) go exec dbo.sp_MS_marksystemobject sp_MSaddmergeschemaarticle raiserror('Creating procedure sp_addmergearticle', 0,1) GO create procedure sp_addmergearticle @publication sysname, /* publication name */ @article sysname, /* article name */ @source_object sysname, /* source object name */ @type sysname = 'table', /* article type */ @description nvarchar(255)= NULL, /* article description */ @column_tracking nvarchar(10) = 'false', /* column level tracking */ @status nvarchar(10) = 'unsynced', /* unsynced, active */ @pre_creation_cmd nvarchar(10) = 'drop', /* 'none', 'drop', 'delete', 'truncate' */ @creation_script nvarchar(255)= NULL, /* article schema script */ @schema_option varbinary(8) = NULL, /* article schema creation options */ @subset_filterclause nvarchar(1000) = '', /* filter clause */ @article_resolver nvarchar(255)= NULL, /* custom resolver for article */ @resolver_info nvarchar(255) = NULL, /* custom resolver info */ @source_owner sysname = NULL, @destination_owner sysname = NULL, @vertical_partition nvarchar(5) = 'FALSE', /* vertical partitioning or not */ @auto_identity_range nvarchar(5) = 'FALSE', /* set it to false for now - change is possible */ @pub_identity_range bigint = NULL, @identity_range bigint = NULL, @threshold int = NULL, @verify_resolver_signature int = 0, /* 0=do not verify signature, 1=verify that signature is from trusted source, more values may be added later */ @destination_object sysname = @source_object, @allow_interactive_resolver nvarchar(5) = 'false', /* whether article allows interactive resolution or not */ @fast_multicol_updateproc nvarchar(5) = 'true', /* whether update proc should update multiple columns in one update statement or not. if 0, then separate update issued for each column changed. */ @check_permissions int = 0, /* bitmap where 0x00 for nochecks, 0x01 for insert check, 0x2 for update check, 0x4 for delete check */ @force_invalidate_snapshot bit = 0 /* Force invalidate existing snapshot */ AS set nocount on /* ** Declarations. */ declare @max_range bigint declare @publisher sysname declare @publisher_db sysname declare @already_published bit declare @identity_so_far bigint declare @ver_partition int declare @sp_resolver sysname declare @num_columns smallint declare @pubid uniqueidentifier /* Publication id */ declare @db sysname declare @identity_support int declare @object sysname declare @owner sysname declare @retcode int declare @objid int declare @sync_objid int declare @typeid smallint declare @nickname int declare @merge_pub_object_bit int declare @column_tracking_id int declare @cmd nvarchar(255) declare @statusid tinyint --1: inactive; 2: active; 5:new_inactive 6:new_active declare @next_seed bigint declare @precmdid int declare @resolver_clsid nvarchar(50) declare @resolver_clsid_old nvarchar(50) declare @tablenick int declare @artid uniqueidentifier declare @i int declare @max_identity bigint declare @colname sysname declare @indid int declare @pkkey sysname declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @dbname sysname declare @replinfo int declare @db_name sysname declare @subset int declare @is_publisher int declare @row_size int declare @sp_name sysname declare @sp_owner sysname declare @qualified_name nvarchar(270) declare @snapshot_ready tinyint declare @sync_mode tinyint declare @allow_interactive_bit bit declare @fast_multicol_updateproc_bit bit declare @additive_resolver sysname declare @average_resolver sysname declare @mindate_resolver sysname declare @needs_pickup bit declare @maxdate_resolver sysname declare @minimum_resolver sysname declare @maximum_resolver sysname declare @mergetxt_resolver sysname declare @pricolumn_resolver sysname declare @xtype int declare @xprec int declare @initial_setting bit declare @bump_to_80 bit declare @gen int declare @replnick int declare @genguid uniqueidentifier declare @guidnull uniqueidentifier declare @dt datetime /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Initializations */ set @guidnull = '00000000-0000-0000-0000-000000000000' select @is_publisher = 0 select @initial_setting = 0 select @needs_pickup = 0 select @bump_to_80 = 0 select @already_published = 0 select @publisher = @@SERVERNAME select @publisher_db = db_name() select @max_identity = NULL select @next_seed = NULL select @statusid = 0 select @resolver_clsid = NULL select @subset = 1 /* Const: publication type 'subset' */ select @merge_pub_object_bit = 128 select @db_name = db_name() select @sp_resolver = 'Microsoft SQLServer Stored Procedure Resolver' select @additive_resolver = 'Microsoft SQL Server Additive Conflict Resolver' select @average_resolver = 'Microsoft SQL Server Averaging Conflict Resolver' select @minimum_resolver = 'Microsoft SQL Server Minimum Conflict Resolver' select @maximum_resolver = 'Microsoft SQL Server Maximum Conflict Resolver' select @mindate_resolver = 'Microsoft SQL Server DATETIME (Earlier Wins) Conflict Resolver' select @maxdate_resolver = 'Microsoft SQL Server DATETIME (Later Wins) Conflict Resolver' select @mergetxt_resolver = 'Microsoft SQL Server Merge Text Columns Conflict Resolver' select @pricolumn_resolver = 'Microsoft SQL Server Priority Column Resolver' if @source_owner is NULL begin select @source_owner = user_name(uid) from sysobjects where id = object_id(@source_object) if @source_owner is NULL begin raiserror (14027, 11, -1, @source_object) return (1) end end select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_object) if @subset_filterclause <> '' and @subset_filterclause is not NULL begin /* check the validity of subset_filterclause */ exec ('declare @test int select @test=1 from ' + @qualified_name + ' where (1=2) and ' + @subset_filterclause) if @@ERROR<>0 begin raiserror(21256, 16, -1, @subset_filterclause, @article) return (1) end end if @destination_owner is NULL select @destination_owner = 'dbo' /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Pad out the specified schema option to the left */ select @schema_option = fn_replprepadbinary8(@schema_option) /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14043, 16, -1, '@publication') return (1) end select @pubid = pubid, @snapshot_ready = snapshot_ready, @sync_mode=sync_mode from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end if lower(@article)='all' begin raiserror(21401, 16, -1) return (1) end /* ** Parameter Check: @type ** If the article is added as a 'indexed view schema only' article, ** make sure that the source object is a schema-bound view. ** Conversely, a schema-bound view cannot be published as a ** 'view schema only' article. */ select @type = lower(@type collate SQL_Latin1_General_CP1_CS_AS) if @type = N'indexed view schema only' and objectproperty(object_id(@qualified_name), 'IsSchemaBound') <> 1 begin raiserror (21277, 11, -1, @qualified_name) return (1) end else if @type = N'view schema only' and objectproperty(object_id(@qualified_name), 'IsSchemaBound') = 1 begin raiserror (21275, 11, -1, @qualified_name) return (1) end /* ** Only publisher can call sp_addmergearticle */ EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @@ERROR <> 0 or @retcode <> 0 BEGIN RAISERROR (20073, 16, -1) RETURN (1) END /* ** Parameter Check: @article. ** Check to see that the @article is local, that it conforms ** to the rules for identifiers, and that it is a table, and not ** a view or another database object. */ if @article is NULL begin raiserror (20045, 16, -1) return (1) end exec @retcode = dbo.sp_MSreplcheck_name @article if @@ERROR <> 0 or @retcode <> 0 return(1) /* ** Set the precmdid. The default type is 'drop'. ** ** @precmdid pre_creation_cmd ** ========= ================ ** 0 none ** 1 drop ** 2 delete ** 3 truncate */ IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('none', 'drop', 'delete', 'truncate') BEGIN RAISERROR (14061, 16, -1) RETURN (1) END /* ** Determine the integer value for the pre_creation_cmd. */ IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'none' select @precmdid = 0 ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'drop' select @precmdid = 1 ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'delete' select @precmdid = 2 ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'truncate' select @precmdid = 3 /* ** Set the typeid. The default type is table. It can ** be one of following. ** ** @typeid type ** ======= ======== ** 0xa table ** 0x20 proc schema only ** 0x40 view schema only ** 0x80 func schema only ** 0x40 indexed view schema only (overloaded) */ IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('table', 'proc schema only', 'view schema only', 'func schema only', 'indexed view schema only') BEGIN RAISERROR (20074, 16, -1) RETURN (1) END IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'table' BEGIN SET @typeid = 0x0a END ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'proc schema only' BEGIN SET @typeid = 0x20 END ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'view schema only' BEGIN SET @typeid = 0x40 END ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'indexed view schema only' BEGIN SET @typeid = 0x40 END ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'func schema only' BEGIN SET @typeid = 0x80 END select @sync_objid = OBJECT_ID(@qualified_name) if @sync_objid is NULL begin raiserror (14027, 11, -1, @qualified_name) return (1) end if @typeid in (0x20,0x40,0x80) begin if exists (select * from syscomments where id = @sync_objid and encrypted = 1) begin raiserror(21004, 16, -1, @source_object) return 1 end end /* ** Parameter Check: @article, @publication. ** Check if the article already exists in this publication. */ IF EXISTS (SELECT * FROM sysmergeextendedarticlesview WHERE pubid = @pubid AND name = @article) BEGIN raiserror (21292, 16, -1, @source_object) RETURN (1) END /* ** At this point, all common parameter validations ** for table and schema only articles have been ** performed, so branch out here to handle schema ** only articles as a special case. */ IF @typeid in (0x20, 0x40, 0x80) BEGIN IF @destination_object IS NULL OR @destination_object = N'' BEGIN SELECT @destination_object = @source_object END IF @schema_option IS NULL BEGIN SELECT @schema_option = 0x0000000000000001 END EXEC @retcode = dbo.sp_MSaddmergeschemaarticle @pubid = @pubid, @article = @article, @source_object = @source_object, @type = @typeid, @description = @description, @status = @status, @pre_creation_command = @precmdid, @creation_script = @creation_script, @source_owner = @source_owner, @destination_owner = @destination_owner, @schema_option = @schema_option, @destination_object = @destination_object, @qualified_name = @qualified_name, @publication = @publication, @snapshot_ready = @snapshot_ready, @force_invalidate_snapshot = @force_invalidate_snapshot RETURN (@retcode) END IF @schema_option IS NULL BEGIN SELECT @schema_option = 0x000000000000CFF1 END /* ** If scheme option contains collation or extended properties, ** bump up the compatibility-level */ -- Since only the lower 32 bits of @schema_option are -- used, the following check is sufficient. Note that @schema_option is -- already padded out to the left at the beginning of this procedure. declare @schema_option_lodword int declare @xprop_schema_option int declare @collation_schema_option int select @xprop_schema_option = 0x00002000 select @collation_schema_option = 0x00001000 select @schema_option_lodword = fn_replgetbinary8lodword(@schema_option) if (@schema_option_lodword & @collation_schema_option) <> 0 begin raiserror(21389, 10, -1, @publication) select @bump_to_80 = 1 end if (@schema_option_lodword & @xprop_schema_option) <> 0 begin raiserror(21390, 10, -1, @publication) select @bump_to_80 = 1 end /* ** Merge table articles does not really support destination object. It has the same value as source */ select @destination_object = @source_object /* select @row_size=sum(length) from syscolumns where id=OBJECT_ID(@qualified_name) if @row_size>6000 begin RAISERROR (21062, 16, -1, @qualified_name) -- RETURN (1) end */ IF LOWER(@vertical_partition collate SQL_Latin1_General_CP1_CS_AS) = 'false' begin select @ver_partition = 0 end else begin select @ver_partition = 1 end select @num_columns=count(*) from syscolumns where id = object_id(@qualified_name) if @num_columns > 246 and LOWER(@vertical_partition collate SQL_Latin1_General_CP1_CS_AS) = 'false' begin RAISERROR (20068, 16, -1, @qualified_name, 246) RETURN (1) end /* ** Get the id of the @qualified_name */ select @objid = id, @replinfo = replinfo from sysobjects where id = OBJECT_ID(@qualified_name) if @objid is NULL begin raiserror (14027, 11, -1, @qualified_name) return (1) end /* ** If current publication contains a non-sync subscription, all articles to be added in it ** has to contain a rowguidcol. */ if exists (select * from sysmergesubscriptions where pubid = @pubid and sync_type = 2) begin if not exists (select * from syscolumns c where c.id=@objid and ColumnProperty(c.id, c.name, 'isrowguidcol') = 1) begin raiserror(20086 , 16, -1, @publication) return (1) end end /* ** If you want to have identity support, @range and threshold can not be NULL */ if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' and (@identity_range is NULL or @threshold is NULL or @pub_identity_range is NULL) begin raiserror(21193, 16, -1) return (1) end if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'false' and (@identity_range is not NULL or @threshold is not NULL or @pub_identity_range is not NULL) begin raiserror(21282, 16, -1) return (1) end if @threshold<0 OR @threshold>100 begin raiserror(21241, 16, -1) return (1) end if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin select @identity_support = 1 if OBJECTPROPERTY(@objid, 'tablehasidentity') <> 1 begin raiserror(21194, 16, -1) return (1) end if @pub_identity_range <= 1 or @identity_range <= 1 begin raiserror(21232, 16 ,-1) return 1 end select @xtype=xtype, @xprec=xprec from syscolumns where id=@objid and columnproperty(id, name, 'IsIdentity')=1 select @max_range = case @xtype when 52 then power((convert(bigint,2)), 8*2-1) - 1 --smallint when 48 then power((convert(bigint,2)), 8-1) - 1 --tinyint when 56 then power((convert(bigint,2)), 8*4-1) - 1 --int when 127 then power((convert(bigint,2)), 62) - 1 + power((convert(bigint,2)), 62) --bigint else power((convert(bigint,2)), 62) -1 + power((convert(bigint,2)), 62) -- defaulted to bigint end if (@xtype=108 or @xtype=106) and @xprec<18 select @max_range = power((convert(bigint,10)), (@xprec+1)) - 1 if @pub_identity_range * 2 + @identity_range > (@max_range - IDENT_CURRENT(@source_object)) begin raiserror(21290, 16, -1) return (1) end end else select @identity_support = 0 /* ** Make sure that the table name specified is a table and not a view. */ if NOT exists (select * from sysobjects where id = (select OBJECT_ID(@qualified_name)) AND type = 'U') begin raiserror (20074, 16, -1) return (1) end /* ** If the table contains one more columns of type bigint or sql_variant, ** and the publication is not of type native mode, we bump up the backward ** compatibility level. */ if @sync_mode=0 and EXISTS (SELECT * FROM syscolumns c WHERE c.id = @sync_objid AND (type_name(c.xtype) = 'bigint' or type_name(c.xtype) = 'sql_variant')) begin raiserror(21357, 10, -1, @publication) select @bump_to_80 = 1 end /* ** 7.0 subscribers do not like data type 'timestamp' */ if EXISTS (select * from syscolumns where id=@sync_objid and type_name(xtype) ='timestamp') begin raiserror(21358, 10, -1, @publication) select @bump_to_80 = 1 end /* ** Validate the column tracking */ if @column_tracking IS NULL OR LOWER(@column_tracking collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@column_tracking') RETURN (1) END if LOWER(@column_tracking collate SQL_Latin1_General_CP1_CS_AS) = 'true' SET @column_tracking_id = 1 else SET @column_tracking_id = 0 if @column_tracking_id=0 and @sync_mode = 1 and @ver_partition = 1 begin RAISERROR (21244, 16, -1) RETURN (1) end /* ** Parameter Check: @allow_interactive_resolver */ if LOWER(@allow_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_interactive_resolver') RETURN (1) END if LOWER(@allow_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) = 'true' set @allow_interactive_bit = 1 else set @allow_interactive_bit = 0 /* ** Parameter Check: @fast_multicol_updateproc */ if LOWER(@fast_multicol_updateproc collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@fast_multicol_updateproc') RETURN (1) END if LOWER(@fast_multicol_updateproc collate SQL_Latin1_General_CP1_CS_AS) = 'true' set @fast_multicol_updateproc_bit = 1 else set @fast_multicol_updateproc_bit = 0 /* ** Get the pubid. */ SELECT @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end execute @retcode = dbo.sp_MSgetreplnick @pubid = @pubid, @nickname = @nickname output if (@@error <> 0) or @retcode <> 0 or @nickname IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 or @distributor is NULL BEGIN RAISERROR (21337, 16, -1) RETURN (1) END /* ** Validate the article resolver */ if @article_resolver IS NOT NULL begin if @article_resolver = 'default' OR @article_resolver = '' begin select @article_resolver = NULL select @resolver_clsid = NULL end else begin /* ** Get the distributor info */ select @distproc = RTRIM(@distributor) + '.master.dbo.xp_regread' EXECUTE @retcode = @distproc 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver', @article_resolver, @param = @resolver_clsid OUTPUT IF @retcode <> 0 or @resolver_clsid IS NULL BEGIN RAISERROR (20020, 16, -1) RETURN (1) END end end /* ** If article resolver is 'SP resolver', make sure that resolver_info refers to an SP or XP; ** Also make sure it is stored with owner qualification */ if @article_resolver = @sp_resolver begin if not exists (select * from sysobjects where id = object_id(@resolver_info) and ( type = 'P' or type = 'X')) begin raiserror(21343, 16, -1, @resolver_info) return (1) end select @sp_name = name, @sp_owner=user_name(uid) from sysobjects where id = object_id(@resolver_info) select @resolver_info = QUOTENAME(@sp_owner) + '.' + QUOTENAME(@sp_name) end /* The following resolvers expect the @resolver_info to be NON NULL */ if @article_resolver = @sp_resolver or @article_resolver = @additive_resolver or @article_resolver = @average_resolver or @article_resolver = @minimum_resolver or @article_resolver = @maximum_resolver or @article_resolver = @mindate_resolver or @article_resolver = @maxdate_resolver or @article_resolver = @mergetxt_resolver or @article_resolver = @pricolumn_resolver begin if @resolver_info IS NULL begin RAISERROR (21301, 16, -1, @article_resolver) return (1) end end /* ** If article resolver uses column names, make sure that resolver_info refers to a valid column. */ if @article_resolver = @pricolumn_resolver or @article_resolver = @additive_resolver or @article_resolver = @average_resolver or @article_resolver = @minimum_resolver or @article_resolver = @maximum_resolver begin if not exists (select * from syscolumns where id = @objid and name=@resolver_info) begin RAISERROR (21501, 16, -1, @article_resolver) return (1) end end /* ** If article resolver is 'mindate/maxdate resolver', make sure that resolver_info refers to a column that is of datatype 'datetime' or smalldatetime */ if @article_resolver = @mindate_resolver or @article_resolver = @maxdate_resolver begin if not exists (select * from syscolumns where id = @objid and name=@resolver_info and type_name(xtype)='datetime' or type_name(xtype) = 'smalldatetime' ) begin RAISERROR (21302, 16, -1, @article_resolver) return (1) end end /* The following resolvers expect the article to be column tracked - warn that the default resolver will be used */ if @article_resolver = @additive_resolver or @article_resolver = @average_resolver or @article_resolver = @mergetxt_resolver begin if @column_tracking_id = 0 begin RAISERROR (21303, 10, -1, @article, @article_resolver) end end if @resolver_info IS NOT NULL and @article_resolver IS NULL begin RAISERROR (21300, 10, -1, @article) set @resolver_info = NULL end /* Make sure that coltracking option matches */ if exists (select * from sysmergearticles where objid = @objid and identity_support <> @identity_support) begin raiserror (21240, 16, -1, @source_object) return (1) end -- Do not allow the table to be published by both merge and queued tran if exists (select * from sysobjects where name = 'syspublications') begin if exists (select * from syspublications p, sysarticles a where p.allow_queued_tran = 1 and p.pubid = a.pubid and a.objid = @objid) begin declare @obj_name sysname select @obj_name = object_name(@objid) raiserror(21266, 16, -1, @obj_name) return (1) end end if exists (select * from sysmergearticles where objid=@objid and pubid in(select pubid from sysmergepublications where UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db)) select @already_published = 1 if @already_published = 1 and LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin raiserror(21359, 10, -1, @publication) select @bump_to_80 = 1 if exists (select * from MSrepl_identity_range where objid=@objid and ((pub_range<>@pub_identity_range) or (range <> @identity_range) or (threshold <> @threshold))) begin raiserror(21291, 16, -1) return (1) end end /* ** Add article to sysmergearticles and update sysobjects category bit. */ begin tran save TRAN sp_addmergearticle /* ** We used to prevent an article from being added to a publication whose snapshot ** has been run already. Now we change this so that it is acceptable by doing reinit. */ if @snapshot_ready > 0 begin if @force_invalidate_snapshot = 0 begin raiserror(21364, 16, -1, @article) goto FAILURE end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 goto FAILURE end /* ** article status 5 or 6 means there is at least one new article after snapshot is ready ** hence all articles added after that point will be new articles as well, regardless of snapshot_ready value. */ if @snapshot_ready>0 or exists (select * from sysmergearticles where pubid=@pubid and (status=5 or status=6)) begin select @needs_pickup=1 end /* ** the case when @already_publisher=1 has been handled outside of the transaction */ if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' and @already_published = 0 begin -- Set the range to negtive if incr of the identity is negtive if IDENT_INCR(@source_object) < 0 begin select @pub_identity_range = -1 * @pub_identity_range; select @identity_range = -1 * @identity_range; end raiserror(21359, 10, -1, @publication) select @bump_to_80 = 1 select @next_seed = next_seed, @max_identity=max_identity from MSrepl_identity_range where objid=@objid select @identity_so_far = 0 if @next_seed is NULL begin select @initial_setting = 1 -- adjust for existing rows, only for original publisher select @is_publisher= 1 --original publisher select @identity_so_far = IDENT_CURRENT(@source_object) if @identity_so_far is NULL begin select @next_seed = IDENT_SEED(@source_object) select @identity_so_far = @next_seed end else select @next_seed = @identity_so_far -- use boundary values by cutting off odds, -- and always give publisher side one more range to allow for existing rows. /* To avoid div by zero errors, error out if pub_range is 0 */ if @pub_identity_range = 0 begin goto FAILURE end select @next_seed = (@next_seed/@pub_identity_range) * @pub_identity_range -- to compensate publisher side an extra range in case it loses some slots by rounding up. -- which only happens when the identity incremental is a positive value if (((@pub_identity_range > 0) and (@identity_so_far > @next_seed)) OR ((@pub_identity_range < 0) and (@identity_so_far < @next_seed))) --to make it symmetric both directions select @next_seed = @next_seed + @pub_identity_range select @max_identity = @max_range --max range decided by data type of identity column insert MSrepl_identity_range(objid, next_seed, pub_range, range, max_identity, threshold, current_max) values (@objid,@next_seed + @pub_identity_range, @pub_identity_range, @identity_range, @max_identity, @threshold, @next_seed + @pub_identity_range) end else begin select @is_publisher=2 -- republisher update MSrepl_identity_range set current_max = @next_seed + @pub_identity_range, pub_range = @pub_identity_range, threshold= @threshold, range = @identity_range where objid=@objid if @@ERROR<>0 goto FAILURE end select @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSinsert_identity' SELECT @dbname = DB_NAME() exec @retcode = @distproc @publisher = @publisher, @publisher_db = @publisher_db, @identity_support=@identity_support, @tablename=@source_object, @pub_identity_range = @pub_identity_range, @identity_range =@identity_range, @threshold =@threshold, @next_seed = @next_seed, @max_identity=@max_identity if @retcode<>0 or @@ERROR<>0 goto FAILURE /* This is to change identity column to 'not for replication' if not having been so already */ select @colname=NULL select @colname = name from syscolumns where id = @objid and colstat & 0x0001 <> 0 and -- is identity colstat & 0x0008 = 0 -- No 'not for repl' property if @colname is not null begin exec @retcode = dbo.sp_replupdateschema @source_object -- Mark 'not for repl' update syscolumns set colstat = colstat | 0x0008 where id = @objid and name = @colname -- Single to refresh the object cache. exec @retcode = dbo.sp_replupdateschema @source_object IF @@ERROR <> 0 OR @retcode <> 0 goto FAILURE end end select @artid = artid from sysmergearticles where objid = @objid select @statusid = 1 /*default status is inactive */ if @artid is NULL begin set @artid = newid() if @@ERROR <> 0 goto FAILURE execute @retcode = dbo.sp_MSgentablenickname @tablenick output, @nickname, @objid if @@ERROR <> 0 OR @retcode <> 0 goto FAILURE end /* Clone the article properties if article has already been published (in a different pub) */ else begin /* ** Parameter Check: @article, @publication. ** Check if the table already exists in this publication. */ if exists (select * from sysmergearticles where pubid = @pubid AND artid = @artid) begin raiserror (21292, 16, -1, @source_object) goto FAILURE end /* Make sure that coltracking option matches */ if exists (select * from sysmergearticles where artid = @artid and column_tracking <> @column_tracking_id) begin raiserror (20030, 16, -1, @article) goto FAILURE end /* Reuse the article nickname if article has already been published (in a different pub)*/ select @tablenick = nickname from sysmergearticles where artid = @artid if @tablenick IS NULL goto FAILURE /* Make sure that @resolver_clsid matches the existing resolver_clsid */ select @resolver_clsid_old = resolver_clsid from sysmergearticles where artid = @artid if ((@resolver_clsid IS NULL AND @resolver_clsid_old IS NOT NULL) OR (@resolver_clsid IS NOT NULL AND @resolver_clsid_old IS NULL) OR (@resolver_clsid IS NOT NULL AND @resolver_clsid_old IS NOT NULL AND @resolver_clsid_old <> @resolver_clsid)) begin raiserror (20037, 16, -1, @article) goto FAILURE end /* Insert to articles, copying some stuff from other article row */ insert into sysmergearticles (name, type, objid, sync_objid, artid, description, pre_creation_command, pubid, nickname, column_tracking, status, creation_script, article_resolver, resolver_clsid, schema_option, destination_object, destination_owner, subset_filterclause, view_type, resolver_info, gen_cur, missing_cols, missing_col_count, excluded_cols, excluded_col_count, identity_support, before_image_objid, before_view_objid, verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions) -- use top 1, distinct could return more than one matching row if status different on partitioned articles select top 1 @article, type, objid, @sync_objid, @artid, @description, @precmdid, @pubid, nickname, column_tracking, @statusid, @creation_script, article_resolver, resolver_clsid, @schema_option, @destination_object, @destination_owner, @subset_filterclause, 0, resolver_info, gen_cur, 0x00, 0, 0x00,0, identity_support, before_image_objid, before_view_objid, verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions from sysmergearticles where artid = @artid /* Jump to end of transaction */ goto DONE_TRAN end /* Add the specific GUID based replication columns to sysmergearticles */ insert sysmergearticles (name, objid, sync_objid, artid, type, description, pubid, nickname, column_tracking, status, schema_option, pre_creation_command, destination_object, destination_owner, article_resolver, resolver_clsid, subset_filterclause, view_type, resolver_info, columns, missing_cols, missing_col_count, excluded_cols, excluded_col_count, identity_support, before_image_objid, before_view_objid, verify_resolver_signature, creation_script, allow_interactive_resolver, fast_multicol_updateproc, check_permissions) values (@article, @objid, @sync_objid, @artid, @typeid, @description, @pubid, @tablenick, @column_tracking_id, @statusid, @schema_option, @precmdid, @destination_object, @destination_owner, @article_resolver, @resolver_clsid, @subset_filterclause, 0, @resolver_info, NULL, 0x00, 0, 0x00,0, @identity_support, NULL, NULL, @verify_resolver_signature, @creation_script, @allow_interactive_bit, @fast_multicol_updateproc_bit, @check_permissions) if @@ERROR <> 0 goto FAILURE exec @retcode = dbo.sp_replupdateschema @qualified_name if @@ERROR <> 0 or @retcode <> 0 goto FAILURE update sysobjects set replinfo = (replinfo | @merge_pub_object_bit) where id = @objid if @@ERROR <> 0 goto FAILURE /* set up the article's gen-cur */ set @genguid = newid() set @dt = getdate() exec @retcode=sp_MSgetreplnick @nickname = @replnick out if @retcode<>0 or @@error<>0 goto FAILURE /* ** If there are no zero generation tombstones or rows, add a dummy row in there. */ if not exists (select * from MSmerge_genhistory) begin begin tran insert into MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values (@genguid, @genguid, 1, 0, @replnick, @dt) if (@@error <> 0) begin goto FAILURE end commit tran end /* Make a generation and update the article's gen_cur */ select @gen = max(gen_cur) from sysmergearticles (updlock holdlock) where nickname = @tablenick and gen_cur is not null if @gen is null begin set @genguid = newid() set @dt = getdate() insert into MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) select @genguid, @guidnull, COALESCE(1 + max(generation), 1), @tablenick, @replnick, @dt from MSmerge_genhistory (updlock) if (@@error <> 0) goto FAILURE select @gen = generation from MSmerge_genhistory where guidsrc = @genguid update sysmergearticles set gen_cur = @gen where nickname = @tablenick if (@@error <> 0) goto FAILURE end /* If the article status is active then prepare the article for merge replication */ if @status = 'active' begin /* Get a holdlock on the underlying table */ select @cmd = 'select * into #tab1 from ' select @cmd = @cmd + @qualified_name select @cmd = @cmd + '(TABLOCK HOLDLOCK) where 1 = 2 ' execute(@cmd) /* Add the guid column to the user table */ execute @retcode = dbo.sp_MSaddguidcolumn @source_owner, @source_object if @@ERROR <> 0 OR @retcode <> 0 -- NOTE: new change goto FAILURE /* Create an index on the rowguid column in the user table */ execute @retcode = dbo.sp_MSaddguidindex @publication, @source_owner, @source_object if @@ERROR <> 0 OR @retcode <> 0 goto FAILURE /* Create the merge triggers on the base table */ execute @retcode = dbo.sp_MSaddmergetriggers @qualified_name, NULL, @column_tracking_id if @@ERROR <> 0 OR @retcode <> 0 goto FAILURE /* Create the merge insert/update stored procedures for the base table */ execute @retcode = dbo.sp_MSsetartprocs @publication, @article if @@ERROR <> 0 OR @retcode <> 0 goto FAILURE /* Set the article status to be active so that Snapshot does not do this again */ select @statusid = 2 /* Active article */ update sysmergearticles set status = @statusid where artid = @artid if @@ERROR <> 0 goto FAILURE end DONE_TRAN: /* identity range control is row level. So one the one is needed for each table */ if @identity_support=1 and @already_published=0 begin exec @retcode = sp_MSreseed @objid=@objid, @next_seed=@next_seed, @range = @pub_identity_range, @is_publisher=@is_publisher, @check_only = 1, @initial_setting = @initial_setting, @bound_value = @identity_so_far if @@ERROR<>0 or @retcode<>0 goto FAILURE end /* ** Set all bits to '1' in the columns column to include all columns. */ IF @ver_partition = 0 --meanning no vertical partition needed. BEGIN EXECUTE @retcode = dbo.sp_mergearticlecolumn @publication=@publication, @article=@article, @schema_replication='true' IF @@ERROR <> 0 OR @retcode <> 0 BEGIN RAISERROR(21198, 16, -1) goto FAILURE END END /* ** Set all bits to '1' for all columns in the primary key. */ ELSE BEGIN SELECT @indid = indid FROM sysindexes WHERE id = @objid AND (status & 2048) <> 0 /* PK index */ /* ** First we'll figure out what the keys are. */ SELECT @i = 1 WHILE (@i <= 16) BEGIN SELECT @pkkey = INDEX_COL(@source_object, @indid, @i) if @pkkey is NULL break EXECUTE @retcode = dbo.sp_mergearticlecolumn @publication, @article, @pkkey, 'add' IF @@ERROR <> 0 OR @retcode <> 0 BEGIN RAISERROR(21198, 16, -1) goto FAILURE END select @i = @i + 1 END /* ** make sure any existing rowguidcol is in the partition. We can not live without it. */ select @colname=NULL select @colname = name from syscolumns where id = @objid and ColumnProperty(@objid, name, 'isrowguidcol') = 1 if @colname is not NULL BEGIN EXECUTE @retcode = dbo.sp_mergearticlecolumn @publication, @article, @colname, 'add' if @@error<>0 or @retcode<>0 goto FAILURE END END exec @retcode = sp_MSfillupmissingcols @publication, @source_object if @retcode<>0 or @@ERROR<>0 goto FAILURE /* ** For articles with subset filter clause - set the pub type to subset */ if len(@subset_filterclause) > 0 begin execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode<>0 goto FAILURE end SELECT @dbname = DB_NAME() SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSadd_article' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @dbname, @publication = @publication, @article = @article, @destination_object = @destination_object, @source_owner = @source_owner, @source_object = @source_object, @description = @description -- @article_id = NULL IF @@ERROR <> 0 or @retcode <> 0 BEGIN goto FAILURE END if @bump_to_80=1 begin exec @retcode = sp_MSBumpupCompLevel @pubid, 40 if @@ERROR<>0 or @retcode<>0 goto FAILURE end if @needs_pickup=1 begin declare @needs_pick_value int select @needs_pick_value=5 --new_inactive status update sysmergearticles set status=@needs_pick_value where artid = @artid and pubid=@pubid if @@ERROR<>0 goto FAILURE /* ** Add the guid column to the user table if needed, cause snapshot_ready>0 would imply ** this article has got a rowguid column. No need to add index, triggers, or procedures ** as snapshot run will take care of those. */ execute @retcode = dbo.sp_MSaddguidcolumn @source_owner, @source_object if @@ERROR <> 0 OR @retcode <> 0 -- NOTE: new change goto FAILURE execute @retcode = dbo.sp_MSaddguidindex @publication, @source_owner, @source_object if @@ERROR <> 0 OR @retcode <> 0 goto FAILURE end COMMIT TRAN /* If the article status is active adding the merge triggers to the base table */ return (0) FAILURE: RAISERROR (20009, 16, -1, @article, @publication) if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION sp_addmergearticle COMMIT TRANSACTION end return (1) go exec dbo.sp_MS_marksystemobject sp_addmergearticle go grant execute on dbo.sp_addmergearticle to public go raiserror('Creating procedure sp_MSchangemergeschemaarticle', 0,1) GO CREATE PROCEDURE sp_MSchangemergeschemaarticle ( @pubid uniqueidentifier, @artid uniqueidentifier, @property sysname, @value nvarchar(2000) ) AS set nocount on /* ** No need to bump the compatibility level ** here as the compatibility level for ** a publication with a schema-only article ** is already 80 */ declare @schema_option binary(8) declare @precmdid tinyint declare @creation_script nvarchar(255) declare @statusid tinyint declare @type tinyint declare @source_object sysname declare @valid_schema_options int /* ** Parameter Check: @property ** Check to make sure that @property is a valid property in ** sysmergeschemaarticles. */ select @property = lower(@property collate SQL_Latin1_General_CP1_CS_AS) if @property NOT IN ('description', 'pre_creation_command', 'creation_script', 'status', 'schema_option', 'destination_owner', 'destination_object') begin raiserror(21224, 16, -1, @property) return (1) end select @type = type, @source_object = object_name(objid) from sysmergeextendedarticlesview where pubid = @pubid and artid = @artid -- Since all property changes will take the form of -- simple update stataments, no transaction will be -- started. if @property = N'description' begin update sysmergeschemaarticles set description = @value where pubid = @pubid and artid = @artid if @@error <> 0 return 1 end else if @property = N'pre_creation_command' begin /* ** The value for the pre_creation_command ** property must be either 'none' or 'drop' */ select @value = lower(@value collate SQL_Latin1_General_CP1_CS_AS) if @value not in (N'none', N'drop') begin raiserror(21223, 16, -1) return (1) end if @value = N'none' select @precmdid = 0 else if @value = N'drop' select @precmdid = 1 update sysmergeschemaarticles set pre_creation_command = @precmdid where pubid = @pubid and artid = @artid if @@error <> 0 return (1) end else if @property = N'creation_script' begin if @value is NULL or @value = N'' begin /* ** Existing schema option must not be ** 0x0000000000000000 if the creation_script ** path has to be set to null */ select @schema_option = schema_option from sysmergeschemaarticles where pubid = @pubid and artid = @artid /* if @schema_option = 0x0000000000000000 begin raiserror(21218, 16, -1) return (1) end */ end update sysmergeschemaarticles set creation_script = @value where pubid = @pubid and artid = @artid if @@error <> 0 return (1) end else if @property = N'status' begin select @value = lower(@value collate SQL_Latin1_General_CP1_CS_AS) if @value not in ('active', 'unsynced') begin raiserror(20075, 16, -1) return (1) end if @value = N'unsynced' select @statusid = 1 else if @value = N'active' select @statusid = 2 update sysmergeschemaarticles set status = @statusid where pubid = @pubid and artid = @artid if @@error <> 0 return (1) end else if @property = N'schema_option' begin if @value is null begin raiserror(14146, 16,1) return (1) end create table #tab_changeschemaarticle (value varbinary(8) NULL) if @@error <> 0 begin return(1) end exec ('insert #tab_changeschemaarticle values (' + @value + ')') if @@error <> 0 begin drop table #tab_changeschemaarticle return (1) end select @schema_option = fn_replprepadbinary8(value) from #tab_changeschemaarticle /* ** schema_option can only contain the bits 0x0000000000000001 and ** 0x0000000000002000 ** for schema only articles except view. View articles can contain ** the options 0x0000000000000010, 0x0000000000000020, and 0x0000000000000100 ** in addition to the aforementioned options. */ if @type = 0x40 begin -- Since only the lower 32 bits of @schema_option are -- currently, the following check is sufficient. declare @schema_option_lodword int select @valid_schema_options = 0x2151 select @schema_option_lodword = fn_replgetbinary8lodword(@schema_option) if (@schema_option_lodword & ~@valid_schema_options) <> 0 begin raiserror (21229, 16, -1) return (1) end end else if @schema_option not in (0x0000000000000000, 0x0000000000000001, 0x0000000000002000, 0x0000000000002001) begin drop table #tab_changeschemaarticle raiserror (21222, 16, -1) return (1) end if exists (select * from #tab_changeschemaarticle where value = 0x0000000000000000) begin select @creation_script = NULL select @creation_script = creation_script from sysmergeschemaarticles where pubid = @pubid and artid = @artid /* if @creation_script is NULL or @creation_script = N'' begin raiserror(21218, 16, -1) drop table #tab_changeschemaarticle return (1) end */ end update sysmergeschemaarticles set schema_option = fn_replprepadbinary8(tab.value) from #tab_changeschemaarticle tab where pubid = @pubid and artid = @artid if @@error <> 0 begin drop table #tab_changeschemaarticle return (1) end drop table #tab_changeschemaarticle if @@error <> 0 return (1) end else if @property = N'destination_owner' begin select @value = rtrim(@value) update sysmergeschemaarticles set destination_owner = @value where pubid = @pubid and artid = @artid if @@error <> 0 return (1) end else if @property = N'destination_object' begin select @value = rtrim(@value) if @value is null or @value = '' begin select @value = @source_object end update sysmergeschemaarticles set destination_object = @value where pubid = @pubid and artid = @artid end return (0) go exec dbo.sp_MS_marksystemobject sp_MSchangemergeschemaarticle go raiserror('Creating procedure sp_changemergearticle', 0,1) GO CREATE PROCEDURE sp_changemergearticle ( @publication sysname, /* Publication name */ @article sysname, /* Article name */ @property sysname = NULL, /* The property to change */ @value nvarchar(2000) = NULL, /* The new property value */ @force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @sp_resolver sysname declare @db_name sysname declare @intvalue bigint declare @artid uniqueidentifier declare @pubid uniqueidentifier declare @artidstr nvarchar(38) declare @object_view sysname declare @vertical int declare @pubidstr nvarchar(38) declare @object sysname declare @owner sysname declare @identity_support int declare @column_list nvarchar(4000) declare @resolver_clsid nvarchar(50) declare @article_resolver nvarchar(255) declare @resolver_clsid_str nvarchar(55) declare @article_resolver_str nvarchar(270) declare @value_str nvarchar(270) declare @retcode int declare @statusid int declare @precmdid tinyint declare @regkey nvarchar(255) declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @snapshot_ready int declare @schemaversion int declare @objid int declare @schemaguid uniqueidentifier declare @schematype int declare @schematext nvarchar(2000) declare @type tinyint declare @allow_interactive_bit int declare @additive_resolver sysname declare @average_resolver sysname declare @mindate_resolver sysname declare @maxdate_resolver sysname declare @minimum_resolver sysname declare @maximum_resolver sysname declare @mergetxt_resolver sysname declare @pricolumn_resolver sysname declare @pre_command int declare @coltrack int declare @qual_object_view nvarchar(140) declare @qual_object nvarchar(140) declare @quoted_object nvarchar(140) declare @bump_to_80 bit declare @schema_option binary(8) declare @old_schema_option binary(8) /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) select @db_name = db_name() select @bump_to_80 = 0 /* ** Check to see if the database has been activated for publication. */ if (select category & 4 FROM master..sysdatabases WHERE name = @db_name collate database_default) = 0 BEGIN RAISERROR (14013, 16, -1) RETURN (1) END /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=@db_name if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @db_name = db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> @db_name BEGIN RAISERROR (20047, 16, -1) RETURN (1) END /* ** Parameter Check: @property. ** If the @property parameter is NULL, print the options. */ if @property IS NULL BEGIN CREATE TABLE #tab1 (properties sysname collate database_default) INSERT INTO #tab1 VALUES ('description') INSERT INTO #tab1 VALUES ('pre_creation_command') INSERT INTO #tab1 VALUES ('creation_script') INSERT INTO #tab1 VALUES ('column_tracking (table article only)') INSERT INTO #tab1 VALUES ('article_resolver (table article only)') INSERT INTO #tab1 VALUES ('resolver_info (table article only)') INSERT INTO #tab1 VALUES ('status') INSERT INTO #tab1 VALUES ('subset_filterclause (table article only)') INSERT INTO #tab1 VALUES ('schema_option') INSERT INTO #tab1 VALUES ('destination_owner') INSERT INTO #tab1 VALUES ('pub_identity_range (table article only)') INSERT INTO #tab1 VALUES ('identity_range (table article only)') INSERT INTO #tab1 VALUES ('threshold (table article only)') INSERT INTO #tab1 VALUES ('verify_resolver_signature') INSERT INTO #tab1 VALUES ('allow_interactive_resolver') INSERT INTO #tab1 VALUES ('check_permissions') select * FROM #tab1 RETURN (0) END /* ** Check to see that the article exists in sysmergearticles. ** Fetch the article identification number. */ if @article IS NULL BEGIN RAISERROR (14043, 16, -1, '@article') RETURN (1) END select @artid = artid, @type = type, @old_schema_option = schema_option, @objid = objid FROM sysmergeextendedarticlesview WHERE name = @article AND pubid = @pubid if @artid IS NULL BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END select @pre_command=pre_creation_command, @identity_support = identity_support from sysmergearticles where pubid=@pubid and artid=@artid /* ** Handle the changing of schema only articles property ** in a different procedure */ if @type in (0x20, 0x40, 0x80) begin exec @retcode = sp_MSchangemergeschemaarticle @pubid = @pubid, @artid = @artid, @property = @property, @value = @value return @retcode end /* ** Parameter Check: @property. ** Check to make sure that @property is a valid property in ** sysmergearticles. */ if @property IS NULL OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT in ('name', 'description', 'pre_creation_command', 'creation_script', 'column_tracking', 'article_resolver', 'resolver_info', 'status', 'subset_filterclause', 'schema_option', 'pub_identity_range', 'identity_range', 'threshold', 'verify_resolver_signature', 'check_permissions', 'allow_interactive_resolver', 'destination_owner') BEGIN RAISERROR (21259, 16, -1, @property) RETURN (1) END -- COMMENT: the following article properties can not be changed at republisher side if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('article_resolver','resolver_info','column_tracking', 'allow_interactive_resolver','verify_resolver_signature') and exists (select * from sysmergearticles where objid=@objid and pubid not in (select pubid from sysmergepublications where UPPER(publisher)=UPPER(@@servername) and publisher_db=@db_name)) BEGIN RAISERROR(21400, 16, -1, @article) return (1) END if @identity_support=0 and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('pub_identity_range','identity_range','threshold') begin RAISERROR(21257, 16, -1, @property, @article) return (1) end exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 or @distributor is NULL BEGIN RAISERROR (20036, 16, -1) RETURN (1) END SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity' select @objid=objid, @object = object_name(objid), @vertical=vertical_partition from sysmergearticles where artid=@artid and pubid=@pubid set @artidstr = '''' + convert(nchar(36), @artid) + '''' set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' select @sp_resolver = 'Microsoft SQLServer Stored Procedure Resolver' select @additive_resolver = 'Microsoft SQL Server Additive Conflict Resolver' select @average_resolver = 'Microsoft SQL Server Averaging Conflict Resolver' select @minimum_resolver = 'Microsoft SQL Server Minimum Conflict Resolver' select @maximum_resolver = 'Microsoft SQL Server Maximum Conflict Resolver' select @mindate_resolver = 'Microsoft SQL Server DATETIME (Earlier Wins) Conflict Resolver' select @maxdate_resolver = 'Microsoft SQL Server DATETIME (Later Wins) Conflict Resolver' select @mergetxt_resolver = 'Microsoft SQL Server Merge Text Columns Conflict Resolver' select @pricolumn_resolver = 'Microsoft SQL Server Priority Column Resolver' BEGIN TRAN save TRAN changemergearticle /* ** Changing of the following properties would require a snapshot rerun.and reinit, if snapshot is ready */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('pre_creation_command', 'creation_script', 'column_tracking', 'subset_filterclause', 'schema_option', 'destination_owner', 'check_permissions') and @snapshot_ready > 0 begin if @pre_command<>1 -- 1 means'drop': which is the only option that support reintialization begin raiserror(21416, 16, -1, @property, @article) goto UNDO end if @force_invalidate_snapshot = 0 begin raiserror(20607, 16, -1) goto UNDO end if @force_reinit_subscription = 0 begin raiserror(20608, 16, -1) goto UNDO end update sysmergepublications set snapshot_ready=2 where pubid=@pubid and snapshot_ready=1 if @@ERROR<>0 GOTO UNDO exec @retcode=sp_MSreinitmergepublication @publication if @@ERROR<>0 or @retcode<>0 GOTO UNDO end /* ** Change the property. */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'column_tracking' BEGIN /* ** Check to make sure that we have a valid type. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14137, 16, -1) goto UNDO END /* ** Update the syssubsetdefintions table with the new column tracking. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' update sysmergearticles set column_tracking = 1 where artid=@artid else update sysmergearticles set column_tracking = 0 where artid=@artid if @@ERROR <> 0 goto UNDO END /* pub_identity_range is not stored at distribution database because it is not interested in this value.*/ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pub_identity_range' BEGIN select @intvalue = convert(bigint, @value) if @intvalue<0 begin raiserror(21232, 16, -1) goto UNDO end update MSrepl_identity_range set pub_range=@intvalue where objid=@objid if @@ERROR<>0 goto UNDO END /* the property of check permissions is a bitmask of which operation do we want to check for*/ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'check_permissions' BEGIN select @intvalue = convert(int, @value) if @intvalue<0 begin raiserror(21232, 16, -1) goto UNDO end update sysmergearticles set check_permissions=@intvalue if @@ERROR<>0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'identity_range' BEGIN select @intvalue = convert(bigint, @value) if @intvalue<0 begin raiserror(21232, 16, -1) goto UNDO end update MSrepl_identity_range set range=@intvalue where objid=@objid if @@ERROR<>0 goto UNDO exec @retcode=@distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@object, @range=@intvalue if @retcode<>0 or @@ERROR<>0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'threshold' BEGIN select @intvalue = convert(int, @value) if @intvalue<0 or @intvalue>100 begin raiserror(21241, 16, -1) goto UNDO end update MSrepl_identity_range set threshold=@intvalue where objid=@objid if @@ERROR<>0 goto UNDO exec @retcode=@distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@object, @threshold=@intvalue if @retcode<>0 or @@ERROR<>0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='description' BEGIN UPDATE sysmergearticles SET description = @value WHERE artid = @artid and pubid = @pubid AND pubid = @pubid if @@ERROR <> 0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='creation_script' BEGIN update sysmergearticles set creation_script=@value where artid=@artid and pubid=@pubid if @@ERROR <> 0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'subset_filterclause' BEGIN if @value is not null and @value<>'' begin /* check the validity of subset_filterclause */ select @object_view=@object select @quoted_object=QUOTENAME(@object) exec @retcode = sp_MSget_qualified_name @objid, @qual_object OUTPUT if @@ERROR<>0 or @retcode<>0 goto UNDO select @qual_object_view=@qual_object if @vertical=1 begin select @object_view='TEMP_VIEW_' + @object select @qual_object_view = QUOTENAME(@object_view) exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @quoted_object) if @@ERROR<>0 begin raiserror(21256, 16, -1, @value, @object) goto UNDO end end exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_object + ' where ' + @value) if @@ERROR<>0 begin if @vertical=1 exec('drop view ' + @qual_object_view) raiserror(21256, 16, -1, @value, @object) goto UNDO end end if @vertical=1 exec('drop view ' + @qual_object_view) update sysmergearticles set subset_filterclause = @value where artid=@artid and pubid=@pubid if @@ERROR<>0 goto UNDO /* ** set the pub type to subset or full as appropriate */ execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 OR @retcode <> 0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='article_resolver' BEGIN if @value IS NULL OR @value = 'default' OR @value = '' begin set @article_resolver = NULL set @resolver_clsid = NULL end else begin EXECUTE @retcode = master.dbo.xp_regread 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver', @value, @param = @resolver_clsid OUTPUT IF @@ERROR <> 0 or @retcode <> 0 or @resolver_clsid IS NULL BEGIN RAISERROR (20020, 16, -1) goto UNDO END end /* ** Update the appropriate column in sysmergearticles with the new article resolver name. ** Note this could affect multiple publication if the same table spans publications */ /* NOTE: new change */ exec @retcode = dbo.sp_MSchangearticleresolver @value, @resolver_clsid, @artid if @@ERROR <> 0 OR @retcode <> 0 begin goto UNDO end declare one_pub CURSOR LOCAL FAST_FORWARD FOR select DISTINCT pubid from sysmergearticles where artid=@artid FOR READ ONLY open one_pub fetch next from one_pub into @pubid while (@@fetch_status <> -1) begin select @snapshot_ready=snapshot_ready from sysmergepublications where pubid = @pubid /* Insert the sp_MSchangearticleresolver schema change only if the publication's snapshot is ready */ if (@snapshot_ready > 0) begin select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @schematype = 8 if @value is not NULL and @value <> '' select @schematext = 'exec dbo.sp_MSchangearticleresolver ' + quotename(@value) + ',' + '''' + @resolver_clsid + '''' + ',' + '''' + convert(nchar(36), @artid) + '''' else select @schematext = 'exec dbo.sp_MSchangearticleresolver NULL, NULL,' + '''' + convert(nchar(36), @artid) + '''' exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR <> 0 OR @retcode <> 0 goto UNDO end fetch next from one_pub into @pubid end close one_pub deallocate one_pub END /* for property = 'article_resolver' */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='resolver_info' BEGIN /* allow non-sp's as resolver info; don't change the resolver class */ select @article_resolver = article_resolver, @resolver_clsid = resolver_clsid from sysmergearticles where artid = @artid -- Convert NULL value to 'NULL' string to be used for constructing schema text -- we do not want to change NULL value to ''. select @article_resolver_str=QUOTENAME(@article_resolver) select @resolver_clsid_str = @resolver_clsid select @value_str=@value if @article_resolver is null set @article_resolver_str = 'NULL' if @resolver_clsid is null set @resolver_clsid_str = 'NULL' if @value is null set @value_str = 'NULL' /* The following resolvers expect the @resolver_info to be NON NULL */ if @article_resolver = @sp_resolver or @article_resolver = @additive_resolver or @article_resolver = @average_resolver or @article_resolver = @minimum_resolver or @article_resolver = @maximum_resolver or @article_resolver = @mindate_resolver or @article_resolver = @maxdate_resolver or @article_resolver = @mergetxt_resolver or @article_resolver = @pricolumn_resolver begin if @value IS NULL or @value = '' begin RAISERROR (21301, 16, -1, @article_resolver) goto UNDO end end /* The SP resolver expect the @resolver_info to be the name of sp */ if @article_resolver = @sp_resolver begin if not exists (select * from sysobjects where id = object_id(@value) and ( type = 'P' or type = 'X')) begin raiserror(21343, 16, -1, @value) goto UNDO end end /* ** If article resolver is 'mindate/maxdate resolver', make sure that resolver_info refers to a column that is of datatype 'datetime' or smalldatetime */ if @article_resolver = @mindate_resolver or @article_resolver = @maxdate_resolver begin if not exists (select * from syscolumns where id = @objid and name=@value and type_name(xtype)='datetime' or type_name(xtype) = 'smalldatetime' ) begin RAISERROR (21302, 16, -1, @article_resolver) goto UNDO end end /* The following resolvers expect the article to be column trcaked - warn that the default resolver migh be used */ if @article_resolver = @additive_resolver or @article_resolver = @average_resolver begin select @coltrack = column_tracking from sysmergearticles where artid = @artid if @coltrack = 0 begin RAISERROR (21303, 10, -1, @article, @article_resolver) end end /* ** Update the appropriate column in sysmergearticles with the new resolver info. ** Note this could affect multiple publication if the same table spans publications */ exec @retcode = dbo.sp_MSchangearticleresolver @article_resolver, @resolver_clsid, @artid, @value if @@ERROR <> 0 OR @retcode <> 0 begin goto UNDO end declare one_pub CURSOR LOCAL FAST_FORWARD FOR select DISTINCT pubid from sysmergearticles where artid=@artid FOR READ ONLY open one_pub fetch next from one_pub into @pubid while (@@fetch_status <> -1) begin select @snapshot_ready=snapshot_ready from sysmergepublications where pubid = @pubid /* Insert the sp_MSchangearticleresolver schema change only if the publication's snapshot is ready */ if (@snapshot_ready > 0) begin select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @schematype = 8 select @schematext = 'exec dbo.sp_MSchangearticleresolver ' + @article_resolver_str + ',' + '''' + @resolver_clsid_str + '''' + ',' + '''' + convert(nchar(36), @artid) + '''' + ',' + '''' + @value_str + '''' exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR <> 0 OR @retcode <> 0 begin goto UNDO end end fetch next from one_pub into @pubid end close one_pub deallocate one_pub END /* for property = 'resolver_info' */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pre_creation_command' BEGIN /* ** Check to make sure that we have a valid pre_creation_cmd. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('none', 'drop', 'delete', 'truncate') BEGIN RAISERROR (14061, 16, -1) goto UNDO END /* ** Determine the integer value for the pre_creation_cmd. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'none' select @precmdid = 0 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'drop' select @precmdid = 1 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'delete' select @precmdid = 2 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'truncate' select @precmdid = 3 /* ** Update the article with the new pre_creation_cmd. */ UPDATE sysmergearticles SET pre_creation_command = @precmdid WHERE artid = @artid AND pubid = @pubid if @@ERROR <> 0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'status' BEGIN /* ** Check to make sure that we have a valid status */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('active', 'unsynced', 'new_inactive', 'new_active') BEGIN RAISERROR (20075, 16, -1) goto UNDO END /* ** Determine the integer value for the type. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'unsynced' select @statusid = 1 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'active' select @statusid = 2 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'new_inactive' select @statusid = 5 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'new_active' select @statusid = 6 /* ** Update the article with the new type. The same base table might be ** in multiple publications - so qualify with pubid. */ UPDATE sysmergearticles SET status = @statusid WHERE artid = @artid and pubid = @pubid if @@ERROR <> 0 goto UNDO END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'destination_owner' BEGIN IF @value IS NULL or @value='' select @value = 'dbo' UPDATE sysmergearticles SET destination_owner = @value WHERE artid = @artid AND pubid = @pubid if @@ERROR <> 0 BEGIN goto UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'schema_option' BEGIN IF @value IS NULL BEGIN RAISERROR(14146, 16,1) goto UNDO END CREATE TABLE #tab_changearticle (value varbinary(8) NULL) IF @@ERROR <> 0 BEGIN goto UNDO END DECLARE @insert_command nvarchar(4000) EXEC('insert #tab_changearticle values (' + @value +')') IF @@ERROR <> 0 BEGIN goto UNDO END SELECT @schema_option = fn_replprepadbinary8(value) FROM #tab_changearticle DECLARE @schema_option_lodword INT DECLARE @old_schema_option_lodword INT DECLARE @xprop_schema_option INT DECLARE @collation_schema_option INT SELECT @xprop_schema_option = 0x00002000 SELECT @collation_schema_option = 0x00001000 SELECT @schema_option_lodword = fn_replgetbinary8lodword(@schema_option) SELECT @old_schema_option_lodword = CONVERT(INT, SUBSTRING(@old_schema_option, 5, 4)) -- Raise warnings only when we are enabling the Shiloh specific -- options IF ((@old_schema_option_lodword & @collation_schema_option) = 0) AND ((@schema_option_lodword & @collation_schema_option) <> 0) BEGIN RAISERROR(21389, 10, -1, @publication) SELECT @bump_to_80 = 1 END IF((@old_schema_option_lodword & @xprop_schema_option) = 0) AND ((@schema_option_lodword & @xprop_schema_option) <> 0) BEGIN RAISERROR(21390, 10, -1, @publication) SELECT @bump_to_80 = 1 END UPDATE sysmergearticles SET schema_option = fn_replprepadbinary8(tab.value) from #tab_changearticle tab WHERE artid = @artid AND pubid = @pubid if @@ERROR <> 0 BEGIN DROP TABLE #tab_changearticle goto UNDO END DROP TABLE #tab_changearticle IF @@ERROR <> 0 BEGIN goto UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'verify_resolver_signature' BEGIN if @value NOT IN ('1', '0') BEGIN raiserror(21344, 16, -1, '"verify_resolver_signature"') goto UNDO END update sysmergearticles set verify_resolver_signature = convert(int, @value) where artid = @artid and pubid = @pubid if @@ERROR<>0 goto UNDO END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_interactive_resolver' BEGIN /* Check to make sure that we have a true/false. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, 'allow_interactive_resolver') goto UNDO END /* Determine the bit value. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' SET @allow_interactive_bit = 1 ELSE SET @allow_interactive_bit = 0 /* Update the subscription with the new 'allow_interactive_resolver' value. */ update sysmergearticles set allow_interactive_resolver = @allow_interactive_bit where artid = @artid and pubid = @pubid IF @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END END IF @bump_to_80=1 BEGIN EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40 IF @@ERROR<>0 or @retcode<>0 GOTO UNDO END /* ** Return succeed. */ COMMIT TRAN RETURN (0) UNDO: if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION changemergearticle COMMIT TRANSACTION end return (1) go exec dbo.sp_MS_marksystemobject sp_changemergearticle go grant execute on dbo.sp_changemergearticle to public go raiserror('Creating procedure sp_MSadjustmergeidentity', 0,1) GO /* ** When calling from the wrapper stored procedure, we make sure that there is one and ** only one of the two input SP is NULL. Therefore, we do no checking here. */ CREATE PROCEDURE sp_MSadjustmergeidentity @publication sysname = NULL, @tablename sysname = NULL AS declare @db_name sysname declare @pubid uniqueidentifier declare @next_seed bigint declare @pub_range bigint declare @objid int declare @qualname nvarchar(270) declare @retcode int declare @user_name sysname declare @range bigint declare @current_max bigint declare @threshold int declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @flag smallint declare @c_max bigint declare @n_seed bigint declare @nickname int declare @identity_support int declare @artid uniqueidentifier declare @identity_so_far bigint declare @tablelevel bit /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) select @db_name=db_name() select @pubid = newid() select @objid = 0 if (@publication is NULL and @tablename is NULL) or (@publication is not NULL and @tablename is not NULL) begin raiserror('internal error on nullibility', 16, -1) return (1) end if @tablename is not NULL begin select @tablelevel = 1 select @objid = id from sysobjects where name=@tablename end else begin select @tablelevel = 0 select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=@db_name end if exists (select * from sysmergearticles where identity_support<>0 and ((pubid=@pubid and @tablelevel=0) or (@tablelevel=1 and objid=@objid))) begin EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) declare one_article CURSOR LOCAL FAST_FORWARD FOR select DISTINCT artid from sysmergearticles where identity_support<>0 and ((pubid=@pubid and @tablelevel=0) or (@tablelevel=1 and objid=@objid)) open one_article fetch one_article into @artid while (@@fetch_status<>-1) begin select @objid=objid, @identity_support=identity_support from sysmergearticles where pubid=@pubid and artid=@artid select @tablename=object_name(@objid) select @user_name=user_name(uid) from sysobjects where id=@objid select @qualname=QUOTENAME(@user_name) + '.' + QUOTENAME(@tablename) select @next_seed=NULL, @range=NULL, @threshold=NULL --null if not being updated later select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0 --make them non-NULL SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity' exec @retcode= @distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@tablename, @range=@range OUTPUT, @current_max=@current_max OUTPUT, @threshold=@threshold OUTPUT, @next_seed = @next_seed OUTPUT, @pub_range=@pub_range OUTPUT if @retcode<>0 or @@ERROR<>0 return (1) select @identity_so_far = IDENT_CURRENT(@tablename) select @flag=1 if ident_incr(@tablename) < 0 select @flag = -1 /* To avoid div by zero errors, error out if pub_range is 0 */ if @pub_range = 0 begin return 1 end if @flag * 100 * (@identity_so_far - (@current_max + 1 - @pub_range))/@pub_range > @threshold begin select @c_max=@next_seed + @pub_range - 1 select @n_seed=@next_seed + @pub_range SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity' exec @retcode=@distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@tablename, @current_max=@c_max, @next_seed = @n_seed if @retcode<>0 or @@ERROR<>0 return (1) exec @retcode=sp_MSreseed @objid, @next_seed, @pub_range, 1 if @@ERROR <> 0 or @retcode<>0 begin raiserror(21197, 16, -1) return (1) end select @next_seed=@next_seed + @pub_range end fetch next from one_article into @artid end end else begin raiserror(21295, 16, -1, @publication) return (1) end GO exec dbo.sp_MS_marksystemobject sp_MSadjustmergeidentity go grant exec on sp_MSadjustmergeidentity to public go /* ** This SP is called to see if merge publication is still allowed for current database. ** Merge publishing is disallowed if current DB subscribes as local/anonymous subscriber ** 1 means OK, 0 for publication not allowed. */ raiserror('Creating procedure sp_helpallowmerge_publication', 0,1) GO CREATE PROCEDURE sp_helpallowmerge_publication AS declare @srvid int declare @db_name sysname /* Select srvid = 0 for the local server name */ select @srvid = 0 select @db_name = db_name() if exists (select name from sysobjects where name='sysmergesubscriptions') if exists (select priority from sysmergesubscriptions where db_name=@db_name and srvid = @srvid and priority=0) begin select 0 RETURN (0) end select 1 GO exec dbo.sp_MS_marksystemobject sp_helpallowmerge_publication go raiserror('Creating procedure sp_helpmergearticle', 0,1) GO CREATE PROCEDURE sp_helpmergearticle ( @publication sysname = '%', /* The publication name */ @article sysname = '%' /* The article name */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int /* ** Create a temporary table to hold all information. */ declare @helpmergearticle TABLE ( id int identity NOT NULL, name sysname collate database_default not null, source_owner sysname collate database_default not null, source_object sysname collate database_default not null, /* converted from objid */ sync_object_owner sysname collate database_default null, sync_object sysname collate database_default null, /* converted from sync_objid */ description nvarchar(255) collate database_default null, status tinyint NULL, creation_script nvarchar(255) collate database_default null, conflict_table nvarchar(270) collate database_default null, article_resolver nvarchar(255) collate database_default null, subset_filterclause nvarchar(1000) collate database_default null, pre_creation_command tinyint NULL, schema_option binary(8) NULL, type smallint NULL, column_tracking int NULL, resolver_info nvarchar(255) collate database_default null, vertical_partition bit NULL, destination_owner sysname collate database_default null, identity_support int NULL, pub_identity_range bigint NULL, identity_range bigint NULL, threshold int NULL, verify_resolver_signature int NULL, destination_object sysname collate database_default not null, allow_interactive_resolver int NULL, fast_multicol_updateproc int NULL, check_permissions int NULL ) /* ** Running sp_help is OK from everywhere, whether enabled for publishing or not */ IF not exists (select * from sysobjects where name= 'sysmergesubscriptions') RETURN (0) /* ** Security Check. To public. */ /* ** Parameter Check: @publication. ** Check to make sure that the publication exists, that it conforms ** to the rules for identifiers, and that it isn't NULL. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END if @publication <> '%' BEGIN if NOT EXISTS (select pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()) BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END END /* ** Parameter Check: @article. ** Check to make sure that the article exists, that it conforms ** to the rules for identifiers, and that it isn't NULL. */ if @article IS NULL BEGIN RAISERROR (14043, 16, -1, '@article') RETURN (1) END if @article <> '%' BEGIN if NOT EXISTS (select * FROM sysmergeextendedarticlesview WHERE name = @article AND pubid IN (select pubid FROM sysmergepublications WHERE name like @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())) BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END END INSERT INTO @helpmergearticle (name, source_owner, source_object, sync_object_owner, sync_object, description, status, creation_script, conflict_table, pre_creation_command, schema_option, type, column_tracking, article_resolver, subset_filterclause, resolver_info, vertical_partition, destination_owner, identity_support, pub_identity_range, identity_range, threshold, verify_resolver_signature, destination_object, allow_interactive_resolver, fast_multicol_updateproc, check_permissions)( select art.name, users1.name, objects.name, users2.name, syncobjects.name, art.description, art.status, art.creation_script, art.conflict_table, art.pre_creation_command, art.schema_option, case when objectproperty(art.objid, 'IsSchemaBound') = 1 and art.type <> 0x80 then 0x0100 | convert(smallint, art.type) else convert(smallint, ISNULL(art.type,0x0a)) end, art.column_tracking, art.article_resolver, art.subset_filterclause, art.resolver_info, art.vertical_partition, art.destination_owner, art.identity_support, ABS(ir.pub_range), ABS(ir.range), ir.threshold, art.verify_resolver_signature, art.destination_object, art.allow_interactive_resolver, art.fast_multicol_updateproc, art.check_permissions FROM sysmergeextendedarticlesview art left outer join MSrepl_identity_range ir on art.objid=ir.objid inner join sysmergepublications pubs on art.pubid = pubs.pubid inner join sysobjects objects on objects.id = art.objid left outer join sysobjects syncobjects on art.sync_objid = syncobjects.id inner join sysusers users1 on objects.uid = users1.uid left outer join sysusers users2 on syncobjects.uid = users2.uid WHERE art.name LIKE @article AND pubs.name LIKE @publication AND UPPER(pubs.publisher) = UPPER(@@servername) AND pubs.publisher_db = db_name()) order by art.nickname desc select * from @helpmergearticle RETURN (0) go exec dbo.sp_MS_marksystemobject sp_helpmergearticle go grant execute on dbo.sp_helpmergearticle to public go raiserror('Creating procedure sp_dropmergearticle', 0,1) GO CREATE PROCEDURE sp_dropmergearticle( @publication sysname, /* The publication name */ @article sysname, /* The article name */ @ignore_distributor bit = 0, @reserved bit = 0, @force_invalidate_snapshot bit = 0 ) AS set nocount on /* ** Declarations. */ declare @db_name sysname declare @cmd nvarchar(255) declare @artid uniqueidentifier declare @snapshot_ready int declare @objid int declare @pubid uniqueidentifier declare @pubidstr nvarchar(38) declare @merge_pub_object_bit int declare @unpublish_bit int declare @retcode int declare @replinfo int declare @dbname sysname declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @object_name sysname declare @uid smallint declare @owner sysname declare @qualified_name nvarchar(270) declare @filterid int declare @proc_name sysname declare @implicit_transaction int declare @close_cursor_at_commit int declare @sync_objid int declare @view_type int declare @type tinyint declare @allow_anonymous int select @close_cursor_at_commit = 0 select @implicit_transaction = 0 /* ** Save setting values first before changing them */ IF (@reserved = 0) BEGIN SELECT @implicit_transaction = @@options & 2 SELECT @close_cursor_at_commit = @@options & 4 SET IMPLICIT_TRANSACTIONS OFF SET CURSOR_CLOSE_ON_COMMIT OFF END /* ** Initializations. */ -- merge uses bit 8 in replinfo select @merge_pub_object_bit = 128 select @unpublish_bit = ~@merge_pub_object_bit /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) select @pubid = pubid, @snapshot_ready=snapshot_ready, @allow_anonymous=allow_anonymous FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END /* ** Once snapshot is ready, do not allow dropping an article except ** when the article is schema-only. */ if @snapshot_ready>0 and (@allow_anonymous=1 or exists (select * from sysmergesubscriptions where pubid=@pubid and subid<>pubid and status=1)) and not exists (select * from sysmergeschemaarticles where pubid = @pubid and name = @article) begin RAISERROR (21338, 16, -1, @article, @publication) RETURN (1) end if @snapshot_ready>0 begin if @force_invalidate_snapshot = 0 begin raiserror(21379, 16, -1, @article, @publication) return (1) end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 return (1) end set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' /* ** Parameter Check: @article. ** If the @article is 'all', drop all articles for the specified ** publication (@publication). */ if LOWER(@article) = 'all' BEGIN declare hC CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergeextendedarticlesview WHERE pubid=@pubid FOR READ ONLY OPEN hC FETCH hC INTO @article WHILE (@@fetch_status <> -1) BEGIN EXECUTE dbo.sp_dropmergearticle @publication, @article, @ignore_distributor = @ignore_distributor, @reserved = 1 FETCH hC INTO @article END CLOSE hC DEALLOCATE hC RETURN (0) END /* ** Parameter Check: @article. ** The @article name must conform to the rules for identifiers. */ if @article IS NULL BEGIN RAISERROR (14043, 16, -1, '@article') RETURN (1) END /* ** Parameter Check: @publication. ** The @publication name must conform to the rules for identifiers. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END /* ** Ascertain the existence of the article. */ select @type = NULL select @type = type , @artid = artid from sysmergeextendedarticlesview where name = @article and pubid = @pubid if @type is NULL begin raiserror (20027, 16, -1, @article) return (1) end -- if all articles are to be dropped, ignore this checking. if @reserved=0 and exists (select * from sysmergesubsetfilters where pubid=@pubid and join_articlename=@article) begin raiserror(21421, 16, -1, @article) return (1) end /* ** Delete article from sysmergearticles and clear publish bit in ** sysobjects. */ begin tran save TRAN dropmergearticle /* ** Remove the corresponding rows from sysmergeschemachange */ DELETE FROM sysmergeschemachange WHERE artid = @artid AND pubid = @pubid if @@ERROR <> 0 goto FAILURE /* ** Removing a schema only article is a lot simpler than ** removing a table article so a different code path is created ** to handle this. */ if @type in (0x20, 0x40, 0x80) begin /* ** Retrieve the object id of the underlying proc or view object. */ select @objid = objid from sysmergeschemaarticles where name = @article and pubid = @pubid /* ** Remove the corresponding record in sysmergeschemaarticles */ delete sysmergeschemaarticles where name = @article and pubid = @pubid /* ** If this is the last schema only article for the underlying ** view or proc object, unmark the 0x200 bit in sysobject.replinfo */ if not exists (select * from sysmergeschemaarticles where objid = @objid) begin select @merge_pub_object_bit = 512 select @unpublish_bit = ~(@merge_pub_object_bit) if not exists (select * from sysobjects where name = 'sysschemaarticles') begin update sysobjects set replinfo = (@unpublish_bit & replinfo) where id = @objid end else if not exists (select * from sysschemaarticles where objid = @objid) begin update sysobjects set replinfo = (@unpublish_bit & replinfo) where id = @objid end end end else begin /* ** Retrieve the object id of the underlying table. */ select @sync_objid = sync_objid, @view_type = view_type, @artid = artid, @objid = objid from sysmergearticles where name = @article AND pubid = @pubid select @replinfo = replinfo, @object_name=name, @owner= user_name(uid) from sysobjects where id = @objid /* ** If this is the last article that refers to the base table, drop the ** triggers and stored procs */ if NOT exists (select * from sysmergearticles WHERE artid = @artid AND pubid <> @pubid) begin /* ** Cleanup the triggers and stored procs */ EXECUTE @retcode = dbo.sp_MSarticlecleanup @artid = @artid, @pubid = @pubid if @@ERROR <> 0 OR @retcode <> 0 BEGIN GOTO FAILURE END /* ** Clear the replication bit in sysobjects. Now merge and transactional level ** uses different replication bit, checking transactional level is not needed. */ select @qualified_name = (QUOTENAME(@owner) + '.' + QUOTENAME(@object_name)) exec @retcode = dbo.sp_replupdateschema @qualified_name if @@ERROR <> 0 OR @retcode <> 0 BEGIN GOTO FAILURE END update sysobjects set replinfo = (replinfo & @unpublish_bit) where id = @objid IF @@ERROR <> 0 goto FAILURE end else begin /* Always drop the article proc's they are not shared among publications */ EXECUTE @retcode = dbo.sp_MSdroparticleprocs @artid = @artid, @pubid = @pubid if @@ERROR <> 0 OR @retcode <> 0 begin goto FAILURE end /* If the article's has a temporary ( view type = 2) or a permanent view (view_type = 1 ) drop the sync object */ if (@view_type = 1 OR @view_type = 2) begin declare @viewname sysname select @viewname = sysobjects.name from sysobjects where ObjectProperty (sysobjects.id, 'IsView') = 1 and ObjectProperty (sysobjects.id, 'IsMSShipped') = 1 and sysobjects.id = @sync_objid if @viewname IS NOT NULL begin set @viewname = QUOTENAME(@viewname) exec ('drop view ' + @viewname) if @@ERROR<>0 GOTO FAILURE end end end /* ** Remove the row from sysmergearticles. */ DELETE FROM sysmergearticles WHERE artid = @artid AND pubid = @pubid if @@ERROR <> 0 BEGIN GOTO FAILURE END /* delete all the filter components that are defined upon the designated article */ select @filterid = min(join_filterid) from sysmergesubsetfilters where artid = @artid AND pubid = @pubid while (@filterid is not null) begin select @proc_name = expand_proc from sysmergesubsetfilters where artid = @artid AND pubid = @pubid and join_filterid = @filterid if (@proc_name IS NOT NULL) and exists (select * from sysobjects where name = @proc_name and type = 'P') begin exec ('drop proc ' + @proc_name) IF @@ERROR <> 0 goto FAILURE end delete from sysmergesubsetfilters where artid = @artid AND pubid = @pubid and join_filterid = @filterid IF @@ERROR <> 0 goto FAILURE select @filterid = min(join_filterid) from sysmergesubsetfilters where artid = @artid AND pubid = @pubid end /* ** set the pub type to subset or full as appropriate */ execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode <> 0 RETURN (1) /* ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC. */ if @ignore_distributor = 0 begin /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 BEGIN goto FAILURE END SELECT @dbname = DB_NAME() SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSdrop_article' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @dbname, @publication = @publication, @article = @article IF @@ERROR <> 0 or @retcode <> 0 BEGIN goto FAILURE END end end COMMIT TRAN /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END RETURN (0) FAILURE: RAISERROR (14047, 16, -1, @article) if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION dropmergearticle COMMIT TRANSACTION end /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END RETURN (1) go exec dbo.sp_MS_marksystemobject sp_dropmergearticle go grant execute on dbo.sp_dropmergearticle to public go raiserror('Creating procedure sp_addmergepublication', 0,1) GO create procedure sp_addmergepublication ( @publication sysname, /* Publication name */ @description nvarchar(255)= NULL, /* Publication description */ @retention int = 14, /* Retention period of 14 days */ @sync_mode nvarchar(10) = 'native', /* (bcp)native, (bcp)character */ @allow_push nvarchar(5) = 'true', /* Pulication allows push subscriptions */ @allow_pull nvarchar(5) = 'true', /* Pulication allows pull subscriptions*/ @allow_anonymous nvarchar(5) = 'false', /* Pulication allows anonymous subscriptions */ @enabled_for_internet nvarchar(5) = 'false', /* Pulication is enabled for internet */ @centralized_conflicts nvarchar(5) = 'true', /* Conflict records stored at publisher : true or false */ @dynamic_filters nvarchar(5) = 'false', /* Will publication be filtered on dynamic clause? */ @snapshot_in_defaultfolder nvarchar(5) = 'true', /* Will keep a copy of the snapshot files to the default location if an alternate folder is specified */ @alt_snapshot_folder nvarchar(255) = NULL, /* Alternate folder for putting the snapshot file for this publication */ @pre_snapshot_script nvarchar(255) = NULL, /* Pre snapshot commands */ @post_snapshot_script nvarchar(255) = NULL, /* Post snapshot commands */ @compress_snapshot nvarchar(5) = 'false', /* Snapshot compression */ @ftp_address sysname = NULL, /* Post 7.0 FTP Properties */ @ftp_port int = 21, /* Post 7.0 FTP Properties */ @ftp_subdirectory nvarchar(255) = NULL, /* Post 7.0 FTP Properties */ @ftp_login sysname = N'anonymous', /* Post 7.0 FTP Properties */ @ftp_password sysname = NULL, /* Post 7.0 FTP Properties */ @conflict_retention int = 14, /* Conflict retention period */ @keep_partition_changes nvarchar(5) = 'false', /* Optimized Partition Updates/Deletes */ @allow_subscription_copy nvarchar(5) = 'false', /* Allow the subscription to be copied */ @allow_synctoalternate nvarchar(5) = 'false', /* Allow the subscription to be synchronize to alternate partners */ @validate_subscriber_info nvarchar(500) = NULL, /* Should we validate that subscriber is using right params? */ @add_to_active_directory nvarchar(5) = 'false', @max_concurrent_merge int = 0, /* value of 0 means no such limit exists */ @max_concurrent_dynamic_snapshots int = 0 /* Maximum number of concurrent dynamic snapshot sessions */ ) as set nocount on /* ** Declarations. */ declare @retcode int /* return code value for procedure execution */ declare @push tinyint /* subscription type is push */ declare @statid tinyint /* status id based on @status */ declare @sync_modeid tinyint /* sync mode id based on @sync_mode */ declare @global tinyint /* subscriber type of loop-back subscription */ declare @db_name sysname /* database name */ declare @srvid int /* Server ID */ declare @nickname int /* replica nickname */ declare @tranpublish_bit smallint /* online publish bit (flag) in sysdatabases */ declare @mergepublish_bit smallint /* merge publish bit (flag) in sysdatabases */ declare @found int /* flag indicating if publication is found */ declare @pubid uniqueidentifier /* Publication identifier */ declare @allow_push_id bit declare @allow_pull_id bit declare @allow_anonymous_id bit declare @dynamic_filters_id bit declare @allow_subscription_copy_id bit declare @allow_synctoalternate_id bit declare @enabled_for_internet_id bit declare @centralized_conflicts_id bit declare @priority real declare @automatic tinyint declare @false bit declare @true bit declare @distributor sysname declare @distproc nvarchar(300) declare @distribdb sysname declare @distpubid int declare @full int declare @snapshot_in_defaultfolder_bit bit declare @compress_snapshot_bit bit declare @keep_before_values_int int declare @enc_ftp_password nvarchar(524) declare @ad_guidname sysname declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematype int declare @schematext nvarchar(2000) declare @artid uniqueidentifier declare @distservername sysname declare @backward_comp_level int /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Initializations */ select @backward_comp_level = 10 --that of 7.0 RTM select @mergepublish_bit = 4 select @tranpublish_bit = 1 select @priority = 100.0 select @automatic = 1 /* Const: synchronization type 'automatic' */ select @true = 1 select @false = 0 select @full = 0 /* Const: publication type 'full' */ /* ** Set the status to Active (1) */ select @statid = 1 select @global = 1 select @push = 0 select @db_name = DB_NAME() select @ad_guidname = NULL /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Parameter Check: @publication. ** The @publication name must conform to the rules for identifiers, ** and must not be the keyword 'all'. */ if @publication is NULL begin raiserror (14043, 16, -1, '@publication') return (1) end exec @retcode = dbo.sp_MSreplcheck_name @publication if @@ERROR <> 0 or @retcode <> 0 return(1) if LOWER (@publication) = 'all' begin raiserror (14034, 16, -1) return (1) end if @max_concurrent_merge<0 begin raiserror(21402, 16, -1, '@max_concurrent_merge') return (1) end /* ** Parameter Check: @retention. */ if @retention is not NULL and @retention<0 begin raiserror(20050, 16, -1, 0) return(1) end if @retention is NULL select @retention = 0 /* ** Parameter Check: @conflict_retention. */ if @conflict_retention is not NULL and @conflict_retention<0 begin raiserror(20050, 16, -1, 0) return(1) end /* ** if it is NULL, use the default value of 14 days. */ if @conflict_retention is NULL select @conflict_retention = 14 /* ** Parameter Check: @sync_mode. ** Make sure that the sync_mode is one of the following: ** ** id sync_mode ** == ========== ** 0 (bcp)native ** 1 (bcp)character */ if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS)='portable' select @sync_mode='character' if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) is NULL OR LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('bcp native', 'bcp character', 'native', 'character') begin raiserror (20076, 16, -1) return (1) end if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) = 'native' or LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS)='bcp native' select @sync_modeid = 0 else select @sync_modeid = 1 /* ** Parameter Check: @allow_push. */ if @allow_push IS NULL OR LOWER(@allow_push collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_push') RETURN (1) END if LOWER(@allow_push collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @allow_push_id = 1 else select @allow_push_id = 0 /* ** Parameter Check: @allow_pull. */ if @allow_pull IS NULL OR LOWER(@allow_pull collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_pull') RETURN (1) END if LOWER(@allow_pull collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @allow_pull_id = 1 else select @allow_pull_id = 0 /* ** Parameter Check: @allow_anonymous. */ if @allow_anonymous IS NULL OR LOWER(@allow_anonymous collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_anonymous') RETURN (1) END if LOWER(@allow_anonymous collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @allow_anonymous_id = 1 else select @allow_anonymous_id = 0 /* ** Parameter Check: @enabled_for_internet. */ IF @enabled_for_internet IS NULL OR LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@enabled_for_internet') RETURN (1) END IF LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @enabled_for_internet_id = 1 ELSE SELECT @enabled_for_internet_id = 0 /* ** Parameter Check: @centralized_conflicts. */ if @centralized_conflicts IS NULL OR LOWER(@centralized_conflicts collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@centralized_conflicts') RETURN (1) END if LOWER(@centralized_conflicts collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @centralized_conflicts_id = 1 else begin raiserror(21349, 10, -1, @publication) select @backward_comp_level = 30 -- that of Sphinx SP2, in which decentrailzed logging will be supported. select @centralized_conflicts_id = 0 end /* ** Parameter Check: @dynamic_filter. */ IF @dynamic_filters IS NULL OR LOWER(@dynamic_filters collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@dynamic_filters') RETURN (1) END IF LOWER(@dynamic_filters collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @dynamic_filters_id = 1 ELSE SELECT @dynamic_filters_id = 0 if @validate_subscriber_info is not NULL begin if @dynamic_filters_id = 0 begin raiserror(21313, 16, -1) return (1) end exec ('select ' + @validate_subscriber_info) if @@ERROR<>0 begin raiserror(21299, 16, -1, @validate_subscriber_info) return (1) end end -- Portable snapshot IF @snapshot_in_defaultfolder IS NULL OR LOWER(@snapshot_in_defaultfolder collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@snapshot_in_defaultfolder') RETURN (1) END IF LOWER(@snapshot_in_defaultfolder collate SQL_Latin1_General_CP1_CS_AS) = 'true' BEGIN SELECT @snapshot_in_defaultfolder_bit = 1 END ELSE BEGIN SELECT @snapshot_in_defaultfolder_bit = 0 END -- Pre/Post snapshot commands -- If @sync_method is character mode bcp, this would indicate that -- this publication may support non-SQL Server subscribers. In this -- case, pre- and post- snapshot commands are not allowed. IF @sync_modeid = 1 AND ((@pre_snapshot_script IS NOT NULL AND @pre_snapshot_script <> N'' ) OR (@post_snapshot_script IS NOT NULL AND @post_snapshot_script <> N'')) BEGIN RAISERROR (21151, 16, -1) RETURN (1) END -- Parameter check - @compress_snapshot -- @compress_snapshot can be 1 if @alt_snapshot_folder is non-null IF @compress_snapshot IS NULL OR LOWER(@compress_snapshot collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@compress_snapshot') RETURN (1) END IF LOWER(@compress_snapshot collate SQL_Latin1_General_CP1_CS_AS) = 'true' BEGIN SELECT @compress_snapshot_bit = 1 END ELSE BEGIN SELECT @compress_snapshot_bit = 0 END -- Only bump up the compatibility level if only a compressed snapshot -- is generated at the alternate snapshot folder if @snapshot_in_defaultfolder_bit = 0 and @compress_snapshot_bit = 1 begin raiserror(21350, 10, -1, @publication) select @backward_comp_level = 40 -- this is supported starting from 7.5 end -- Snapshot compression can only be enabled if an alternate -- snapshot generation folder exists. IF (@compress_snapshot_bit = 1 AND (@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = N'')) BEGIN RAISERROR (21157, 16, -1) RETURN (1) END -- Parameter check: ftp_address -- If the publication is enabled for internet, ftp_address cannot be null IF @enabled_for_internet_id = 1 AND (@ftp_address IS NULL OR @ftp_address = N'') BEGIN RAISERROR (21158, 16, -1) RETURN (1) END -- Parameter check: enabled_for_internet -- If a publication is enabled for internet, it must have an alternate -- snapshot folder defined. IF LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND (@alt_snapshot_folder = N'' OR (@alt_snapshot_folder IS NULL)) BEGIN RAISERROR (21159, 16, -1) RETURN (1) END -- Parameter check: ftp_port IF @ftp_port IS NULL BEGIN RAISERROR (21160, 16, -1) END -- Encrypt ftp password before putting it into the sysmergepublications -- table if one is provided SELECT @enc_ftp_password = NULL IF @ftp_password IS NOT NULL BEGIN SELECT @enc_ftp_password = @ftp_password EXEC @retcode = master.dbo.xp_repl_encrypt @enc_ftp_password OUTPUT IF @retcode <> 0 BEGIN RETURN (1) END END /* ** Parameter Check: @keep_partition_changes. */ if LOWER(@keep_partition_changes collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@keep_partition_changes') RETURN (1) END if LOWER(@keep_partition_changes collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin set @keep_before_values_int = 1 end else set @keep_before_values_int = 0 /* ** Parameter Check: @allow_subscription_copy_id. */ if LOWER(@allow_subscription_copy collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_subscription_copy') RETURN (1) END IF LOWER(@allow_subscription_copy collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @allow_subscription_copy_id = 1 ELSE SELECT @allow_subscription_copy_id = 0 /* ** Parameter Check: @allow_synctoalternate_id. */ if LOWER(@allow_synctoalternate collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@allow_synctoalternate') RETURN (1) END IF LOWER(@allow_synctoalternate collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @allow_synctoalternate_id = 1 ELSE SELECT @allow_synctoalternate_id = 0 /* ** Parameter Check: @add_to_active_directory. */ if LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@add_to_active_directory') RETURN (1) END /* Is AD supported? */ DECLARE @retval INT EXECUTE @retval = master.dbo.xp_MSADEnabled if (@retval <> 0) and LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS)='true' begin RAISERROR(21253, 16, -1) RETURN (1) end /* ** Parameter Check: @max_concurrent_dynamic_snapshots */ if @max_concurrent_dynamic_snapshots < 0 or @max_concurrent_dynamic_snapshots is null begin raiserror(21403, 16, -1) return (1) end /* ** Check to see if the publication name is already used. ** 1. check merge pubs ** 2. check online publications */ if exists (select * from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()) begin RAISERROR (20025, 16, -1, @publication) RETURN (1) end if (select category & @tranpublish_bit from master..sysdatabases where name = @db_name collate database_default) <> 0 begin EXEC @retcode = dbo.sp_helppublication @publication, @found output if @@ERROR <> 0 OR @retcode <> 0 BEGIN RETURN (1) END if @found <> 0 BEGIN RAISERROR (20025, 16, -1, @publication) RETURN (1) END end /* ** Add the publication as the designmaster of the replica set. */ /* Generate a guid for the publication ID */ set @pubid = newid() /* Select the server's ID as 0 since this is the LOCAL server */ select @srvid = 0 /* Look for existing nickname from any other subscription */ exec @retcode=sp_MSgetreplnick NULL, NULL , NULL, @nickname out if (@@error <> 0) or @retcode <> 0 begin RETURN(1) end /* Generate a new replica nickname from the @pubid */ if (@nickname is null) begin execute @retcode = dbo.sp_MSgenreplnickname @pubid, @nickname output IF @@ERROR <>0 OR @retcode <> 0 BEGIN RAISERROR (20077, 16, -1) RETURN (1) END end else select @priority=max(priority) from sysmergesubscriptions where db_name=@db_name and srvid = @srvid /* ** A change in design. */ if @priority = 0 begin RAISERROR(21087, 16, -1) return (1) end /* ** Get distributor information */ EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUT , @distributor = @distservername OUTPUT, @distribdb = @distribdb OUTPUT if @@error <> 0 OR @retcode <> 0 or @distributor IS NULL OR @distribdb IS NULL BEGIN RAISERROR (14071, 16, -1) RETURN (1) END /* ** add an entry into sysmergepublications */ begin tran save tran sp_addmergepublication /* Add row in the publications table */ if @backward_comp_level > 10 begin exec sp_MSBumpupCompLevel @pubid, @backward_comp_level if @@ERROR<>0 goto FAILURE end insert sysmergepublications (pubid, name, description, designmasterid, retention, parentid, sync_mode, allow_push, allow_pull, allow_anonymous, centralized_conflicts, status, snapshot_ready, enabled_for_internet, publication_type, dynamic_filters, snapshot_in_defaultfolder, alt_snapshot_folder, pre_snapshot_script, post_snapshot_script, compress_snapshot, ftp_address, ftp_port, ftp_subdirectory, ftp_login, ftp_password, conflict_retention, keep_before_values, allow_subscription_copy, allow_synctoalternate, validate_subscriber_info, ad_guidname, backward_comp_level, max_concurrent_merge, max_concurrent_dynamic_snapshots) values (@pubid, @publication, @description, @pubid, @retention, @pubid, @sync_modeid, @allow_push_id, @allow_pull_id, @allow_anonymous_id, @centralized_conflicts_id, @statid, @false, @enabled_for_internet_id, @full, @dynamic_filters_id, @snapshot_in_defaultfolder_bit, @alt_snapshot_folder, @pre_snapshot_script, @post_snapshot_script, @compress_snapshot_bit, @ftp_address, @ftp_port, @ftp_subdirectory, @ftp_login, @enc_ftp_password, @conflict_retention, @keep_before_values_int, @allow_subscription_copy_id, @allow_synctoalternate_id, @validate_subscriber_info, @ad_guidname, @backward_comp_level, @max_concurrent_merge, @max_concurrent_dynamic_snapshots) if @@ERROR <> 0 begin goto FAILURE end /* Add row to represent reciprocal subscription */ insert sysmergesubscriptions(subid, partnerid, datasource_type, srvid, db_name, status, priority, pubid, subscriber_type, subscription_type, sync_type, login_name, subscriber_server, publication, distributor, last_validated) values (@pubid, @pubid, 0, @srvid, @db_name, @statid, @priority, @pubid, @global, @push, @automatic, suser_sname(suser_sid()), @@servername, @publication, @distservername, getdate()) if @@ERROR <> 0 begin goto FAILURE end /* ** Add row for merge publication to MSmerge_replinfo. */ insert MSmerge_replinfo(repid, replnickname) values (@pubid, @nickname) if @@ERROR <> 0 begin goto FAILURE end /* ** Add the publication to the distributor side */ SELECT @distpubid = @nickname select @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSadd_publication' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @db_name, @publication = @publication, --@publication_id = NULL, @publication_type = 2, -- 0 = Trans, 1 = Snapshot, 2 = Merge @independent_agent = @true, @immediate_sync = @true, @allow_push = @allow_push_id, @allow_pull = @allow_pull_id, @allow_anonymous = @allow_anonymous_id, --@snapshot_agent = NULL, --@logreader_agent = NULL, @description = @description, @retention = @retention, @allow_subscription_copy = @allow_subscription_copy_id IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO FAILURE END -- Populate the initial list. exec @retcode = dbo.sp_grant_publication_access @publication = @publication, @login = null, @reserved = 'init' IF @@error <> 0 OR @retcode <> 0 GOTO FAILURE commit tran --Put this part outside of a TRANSACTION. It can fail without having to affect publication creation. declare @returnstring nvarchar(512) set @returnstring = N'' if LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS)='true' begin --no error checking needed here. create table #guid_name_for_active_directory(ad_guidname sysname collate database_default null) if @@ERROR<>0 goto SKIP_AD insert into #guid_name_for_active_directory exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'CREATE', 'PUBLICATION', @publication, @db_name if @retcode <> 0 or @@ERROR<>0 begin set @returnstring = (select TOP 1 ad_guidname from #guid_name_for_active_directory) goto SKIP_AD end select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_active_directory if @ad_guidname is not NULL begin update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid if @@ERROR<>0 goto SKIP_AD end drop table #guid_name_for_active_directory end return (0) SKIP_AD: drop table #guid_name_for_active_directory if @returnstring is NULL select @returnstring = N'' raiserror(21363, 16, -1, @publication, @returnstring) return (1) FAILURE: RAISERROR (14018, 16, -1) /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION sp_addmergepublication COMMIT TRANSACTION end return (1) go exec dbo.sp_MS_marksystemobject sp_addmergepublication go grant execute on dbo.sp_addmergepublication to public go raiserror('Creating procedure sp_changemergepublication', 0,1) GO CREATE PROCEDURE sp_changemergepublication ( @publication sysname, /* Publication name */ @property sysname = NULL, /* The property to change */ @value nvarchar(255) = NULL, /* The new property value */ @force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @max_concurrent_merge int declare @cmd nvarchar(255) declare @pubid uniqueidentifier declare @pubidstr nvarchar(38) declare @retcode int declare @retention int declare @statusid tinyint declare @sync_modeid tinyint declare @distributor sysname declare @distproc nvarchar(300) declare @value_bit bit declare @snapshot_ready tinyint declare @subscribed int declare @dbname sysname declare @distribdb sysname declare @alt_snapshot_folder nvarchar(255) declare @enabled_for_internet bit declare @ftp_address sysname declare @enc_ftp_password nvarchar(524) declare @snapshot_in_defaultfolder bit declare @dynamic_filters_id int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematype int declare @schematext nvarchar(2000) declare @artid uniqueidentifier declare @in_ActiveD bit declare @ad_guidname sysname declare @db_name sysname declare @compress_snapshot bit declare @numeric_value int /* ** Initializations */ select @subscribed = 1 select @snapshot_ready = 1 select @db_name=db_name() /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Check if current DB is enabled for publication/subscription */ /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Parameter Check: @property. ** If the @property parameter is NULL, print the options. */ if @property IS NULL BEGIN CREATE TABLE #tab1 (properties sysname collate database_default) INSERT INTO #tab1 VALUES ('description') INSERT INTO #tab1 VALUES ('status') INSERT INTO #tab1 VALUES ('retention') INSERT INTO #tab1 VALUES ('sync_mode') INSERT INTO #tab1 VALUES ('allow_push') INSERT INTO #tab1 VALUES ('allow_pull') INSERT INTO #tab1 VALUES ('allow_anonymous') INSERT INTO #tab1 VALUES ('enabled_for_internet') INSERT INTO #tab1 VALUES ('centralized_conflicts') INSERT INTO #tab1 VALUES ('snapshot_ready') INSERT INTO #tab1 VALUES ('snapshot_in_defaultfolder') INSERT INTO #tab1 VALUES ('alt_snapshot_folder') INSERT INTO #tab1 VALUES ('pre_snapshot_script') INSERT INTO #tab1 VALUES ('post_snapshot_script') INSERT INTO #tab1 VALUES ('compress_snapshot') INSERT INTO #tab1 VALUES ('ftp_address') INSERT INTO #tab1 VALUES ('ftp_port') INSERT INTO #tab1 VALUES ('ftp_subdirectory') INSERT INTO #tab1 VALUES ('ftp_login') INSERT INTO #tab1 VALUES ('ftp_password') INSERT INTO #tab1 VALUES ('conflict_retention') INSERT INTO #tab1 VALUES ('allow_subscription_copy') INSERT INTO #tab1 VALUES ('allow_synctoalternate') INSERT INTO #tab1 VALUES ('validate_subscriber_info') INSERT INTO #tab1 VALUES ('publish_to_ActiveDirectory') INSERT INTO #tab1 VALUES ('dynamic_filters') INSERT INTO #tab1 VALUES ('max_concurrent_merge') INSERT INTO #tab1 VALUES ('max_concurrent_dynamic_snapshots') select * FROM #tab1 RETURN (0) END if @value is NULL and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) not in ('description', 'alt_snapshot_folder', 'pre_snapshot_script', 'post_snapshot_script', 'ftp_address', 'ftp_subdirectory', 'ftp_login', 'ftp_password', 'max_concurrent_merge') begin RAISERROR (20081, 16, -1, @property) RETURN (1) end /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END select @pubid = pubid, @ad_guidname=ad_guidname, --with value NULL if this publication is not in AD. @snapshot_ready=snapshot_ready, @dynamic_filters_id=dynamic_filters, @sync_modeid = sync_mode, @alt_snapshot_folder = alt_snapshot_folder, @enabled_for_internet = enabled_for_internet, @ftp_address = ftp_address, @snapshot_in_defaultfolder = snapshot_in_defaultfolder, @compress_snapshot = compress_snapshot, @in_ActiveD = case when ad_guidname is NULL then 0 else 1 end FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END else /* ** Parameter Check: @property. ** Check to make sure that @property is a valid property in ** sysmergepublications. */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('description', 'status', 'retention', 'sync_mode', 'allow_push', 'allow_pull', 'allow_anonymous', 'enabled_for_internet', 'centralized_conflicts', 'snapshot_ready', 'snapshot_in_defaultfolder', 'alt_snapshot_folder', 'pre_snapshot_script', 'post_snapshot_script', 'compress_snapshot', 'ftp_address', 'ftp_port', 'ftp_subdirectory','ftp_login', 'ftp_password', 'conflict_retention', 'allow_subscription_copy', 'allow_synctoalternate', 'validate_subscriber_info','publish_to_activedirectory','dynamic_filters','max_concurrent_merge', 'max_concurrent_dynamic_snapshots') BEGIN RAISERROR (21053, 16, -1) RETURN (1) END /* ** Parameter Check: ** If sync_method of the publication is character mode (an indication that it supports ** third party Subscribers), pre/post-snapshot setting must be null ** */ IF @sync_modeid = 1 BEGIN IF (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pre_snapshot_script' OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'post_snapshot_script') AND @value IS NOT NULL AND @value <> '' BEGIN RAISERROR (21151, 16, -1) RETURN (1) END END /* ** Parameter Check: ** If the Publication's alt_snapshot_folder setting is null ** snapshot compression cannot be enabled */ IF ((@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = '')) AND LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'compress_snapshot' AND LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' BEGIN RAISERROR (21157, 16, -1) RETURN (1) END /* ** Parameter Check: ** If enabled_for_internet is set to true, the publication must have a non-null ** ftp_address. */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'enabled_for_internet' AND LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND (@ftp_address IS NULL OR @ftp_address = N'') BEGIN RAISERROR(21158, 16, -1) RETURN (1) END /* ** .. and ftp_address cannot be null if the publication is enabled for ** internet. */ /* IF @enabled_for_internet = 1 AND (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_address' AND (@value IS NULL OR @value = N'')) BEGIN RAISERROR(21158, 16, -1) RETURN (1) END */ /* ** .. and 'alternate snapshot folder' is not null and ** 'snapshot in default folder' is false ** */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'enabled_for_internet' AND LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND (@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = N'') BEGIN RAISERROR(21159, 16, -1) RETURN (1) END /* IF @enabled_for_internet = 1 AND (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'alt_snapshot_folder' AND (LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IS NULL OR LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N'')) BEGIN RAISERROR(21159, 16, -1) RETURN (1) END */ if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='validate_subscriber_info' begin if exists (select * from sysmergesubscriptions where pubid=@pubid and subid<>@pubid and status<>0) begin raiserror(21501, 16, -1) return (1) end end /* ** Parameter Check: ** 'ftp_port' cannot be null */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_port' AND @value IS NULL BEGIN RAISERROR (14043, 16, -1, @property) RETURN (1) END BEGIN TRAN changemergepublication save tran changemergepublication /* ** Changing of the following properties would require a snapshot rerun, if snapshot is ready */ if (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) like 'ftp%' OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('sync_mode', 'snapshot_in_defaultfolder', 'alt_snapshot_folder', 'pre_snapshot_script', 'post_snapshot_script','compress_snapshot')) and @snapshot_ready>0 begin if @force_invalidate_snapshot = 0 begin raiserror(20607, 16, -1) goto UNDO end update sysmergepublications set snapshot_ready=2 where pubid=@pubid and snapshot_ready=1 if @@ERROR<>0 GOTO UNDO end if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='dynamic_filters' and @snapshot_ready>0 begin if @force_invalidate_snapshot = 0 begin raiserror(20607, 16, -1) goto UNDO end if @force_reinit_subscription = 0 begin raiserror(20608, 16, -1) goto UNDO end if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @dynamic_filters_id = 1 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'false' select @dynamic_filters_id = 0 else begin raiserror(14137, 16, -1) goto UNDO end update sysmergepublications set dynamic_filters=@dynamic_filters_id, snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 goto UNDO exec @retcode = sp_MSreinitmergepublication @publication if @retcode<>0 or @@ERROR<>0 goto UNDO end if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='validate_subscriber_info' begin if @value is not NULL begin if @dynamic_filters_id = 0 begin raiserror(21313, 16, -1) GOTO UNDO end exec ('select ' + @value) if @@ERROR<>0 begin raiserror(21299, 16, -1, @value) GOTO UNDO end end UPDATE sysmergepublications SET validate_subscriber_info = @value WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO end if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description') BEGIN UPDATE sysmergepublications SET description = @value WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'status' BEGIN /* ** Check to make sure that we have a valid status. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('active', 'inactive') BEGIN RAISERROR (14012, 16, -1) GOTO UNDO END /* ** Determine the integer value for the status. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'active' select @statusid = 1 else select @statusid = 0 /* ** Update the publication with the new status. */ UPDATE sysmergepublications SET status = @statusid WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'retention' BEGIN /* ** Update the publication with the new replication frequency. */ select @retention = CONVERT(int, @value) if @retention is NULL select @retention = 0 if @retention < 0 begin raiserror(20050, 16, -1, 0) GOTO UNDO end UPDATE sysmergepublications set retention = @retention WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @artid = newid() set @schematype = 9 /* change retention */ select @schematext = 'exec dbo.sp_MSchange_retention '+ '''' + convert(nchar(36),@pubid) + '''' + ',' + '''' + @value + '''' exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 goto UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'conflict_retention' BEGIN /* ** Update the publication with the new conflict_retention value */ select @retention = CONVERT(int, @value) if @@ERROR<>0 GOTO UNDO if @value is NULL select @retention = 0 if @retention < 0 begin raiserror(20050, 16, -1, 0) GOTO UNDO end UPDATE sysmergepublications SET conflict_retention = @retention WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'max_concurrent_merge' BEGIN /* ** Update the publication property <max_concurrent_merge> with a new value */ select @max_concurrent_merge = CONVERT(int, @value) if @max_concurrent_merge < 0 begin raiserror(21402, 16, -1, '@value') GOTO UNDO end UPDATE sysmergepublications SET max_concurrent_merge = @max_concurrent_merge WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'sync_mode' BEGIN /* ** Check for a valid synchronization method. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='portable' select @value='character' if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('native', 'character', 'bcp native', 'bcp character') begin raiserror (20076, 16, -1) GOTO UNDO end /* ** Determine the integer value for the sync_mode. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IN ('native', 'bcp native') select @sync_modeid = 0 else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IN ('character', 'bcp character') select @sync_modeid = 1 /* ** Update the publication with the new synchronization method. */ UPDATE sysmergepublications SET sync_mode = @sync_modeid WHERE pubid = @pubid if @@ERROR <> 0 GOTO UNDO END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'alt_snapshot_folder' BEGIN -- If the alt_snapshot_folder is set to '' or NULL, -- set the compress_snapshot bit to 0 and disable internet -- support IF @value IS NULL OR @value = N'' BEGIN UPDATE sysmergepublications SET alt_snapshot_folder = @value, compress_snapshot = 0, enabled_for_internet = 0 WHERE pubid = @pubid END ELSE BEGIN UPDATE sysmergepublications SET alt_snapshot_folder = @value WHERE pubid = @pubid END IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'pre_snapshot_script' BEGIN UPDATE sysmergepublications SET pre_snapshot_script = @value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'post_snapshot_script' BEGIN UPDATE sysmergepublications SET post_snapshot_script = @value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_address' BEGIN IF @value IS NULL OR @value = N'' BEGIN UPDATE sysmergepublications SET ftp_address = @value, enabled_for_internet = 0 WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END ELSE BEGIN UPDATE sysmergepublications SET ftp_address = @value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_port' BEGIN UPDATE sysmergepublications SET ftp_port = CONVERT(int, @value) WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_subdirectory' BEGIN UPDATE sysmergepublications SET ftp_subdirectory = @value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_login' BEGIN UPDATE sysmergepublications SET ftp_login = @value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_password' BEGIN SELECT @enc_ftp_password = NULL IF @value IS NOT NULL BEGIN SELECT @enc_ftp_password = @value EXEC @retcode = master.dbo.xp_repl_encrypt @enc_ftp_password OUTPUT IF @retcode <> 0 BEGIN GOTO UNDO END END UPDATE sysmergepublications SET ftp_password = @enc_ftp_password WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'max_concurrent_dynamic_snapshots' BEGIN SELECT @numeric_value = CONVERT(int, @value) IF @@ERROR<>0 OR @numeric_value < 0 OR @numeric_value IS NULL BEGIN RAISERROR(21403, 16, -1) GOTO UNDO END UPDATE sysmergepublications SET max_concurrent_dynamic_snapshots = @numeric_value WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'publish_to_activedirectory' BEGIN if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14137, 16, -1) GOTO UNDO END /* Is AD supported? */ DECLARE @retval INT EXECUTE @retval = master.dbo.xp_MSADEnabled if (@retval <> 0) begin RAISERROR(21254, 16, -1, @publication) RETURN (1) end if @in_ActiveD=0 and LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='true' BEGIN create table #guid_name_for_active_directory(ad_guidname sysname collate database_default null) if @@ERROR<>0 begin raiserror(21363, 16, -1, @publication, N'') goto UNDO end insert into #guid_name_for_active_directory exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'CREATE', 'PUBLICATION', @publication, @db_name if @retcode <> 0 or @@ERROR<>0 begin declare @errorstring nvarchar(512) select @errorstring = (select TOP 1 ad_guidname from #guid_name_for_active_directory) drop table #guid_name_for_active_directory if @errorstring is NULL select @errorstring=N'' raiserror(21363, 16, -1, @publication, @errorstring) GOTO UNDO end select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_active_directory if @ad_guidname is not NULL begin update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid if @@ERROR<>0 begin drop table #guid_name_for_active_directory raiserror(21363, 16, -1, @publication, N'') goto UNDO end end drop table #guid_name_for_active_directory END else if @in_ActiveD=1 and LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='false' BEGIN exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'DELETE', 'PUBLICATION', @publication, @db_name, @ad_guidname if @@ERROR<>0 or @retcode<>0 begin raiserror(21369, 16, -1, @publication) goto UNDO end update sysmergepublications set ad_guidname=NULL where pubid=@pubid if @@ERROR<>0 begin raiserror(21369, 16, -1, @publication) goto UNDO end END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('allow_push', 'allow_pull', 'allow_anonymous', 'enabled_for_internet', 'centralized_conflicts', 'snapshot_ready', 'snapshot_in_defaultfolder', 'compress_snapshot', 'allow_subscription_copy', 'allow_synctoalternate') BEGIN /* ** Check for a valid value. */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14137, 16, -1) GOTO UNDO END /* ** set value bit */ if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @value_bit = 1 else select @value_bit = 0 if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_anonymous' BEGIN /* Update the allow_anonymous column */ UPDATE sysmergepublications SET allow_anonymous = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_push' BEGIN /* Update the allow_push column */ UPDATE sysmergepublications SET allow_push = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_pull' BEGIN /* Update the allow_pull column */ UPDATE sysmergepublications SET allow_pull = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'centralized_conflicts' BEGIN /* Update the centralized_conflicts column */ UPDATE sysmergepublications SET centralized_conflicts = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END if @value_bit=0 and @snapshot_ready>0 begin -- changing to decentralized conflict logging will require reinitialization but no snapshot rerun if @force_reinit_subscription = 0 begin raiserror(20608, 16, -1) goto UNDO end exec @retcode = sp_MSreinitmergepublication @publication if @@ERROR<>0 or @retcode<>0 goto UNDO end END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'enabled_for_internet' BEGIN UPDATE sysmergepublications SET enabled_for_internet = @value_bit WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'snapshot_ready' BEGIN /* Update the allow_anonymous column */ UPDATE sysmergepublications SET snapshot_ready = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'snapshot_in_defaultfolder' BEGIN -- snapshot_in_defaultfolder = 1 is only meaningful when -- alt_snapshot_folder is non-null, otherwise -- a copy of the snapshot files is always kept -- at the publisher's working directory UPDATE sysmergepublications SET snapshot_in_defaultfolder = @value_bit WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END -- Bump up the compatibility level if we are -- setting snapshot_in_defaultfolder to 0 -- and compression is enabled. i.e. only -- a compressed snapshot will be generated -- Note that alt_snapshot_folder is implicitly -- specified for the publication IF @value_bit = 0 and @compress_snapshot = 1 BEGIN EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40 IF @@ERROR<>0 OR @retcode<>0 GOTO UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'compress_snapshot' BEGIN UPDATE sysmergepublications SET compress_snapshot = @value_bit WHERE pubid = @pubid IF @@error <> 0 BEGIN GOTO UNDO END -- See comment for 'snapshot_in_defaultfolder' IF @value_bit = 1 and @snapshot_in_defaultfolder = 0 BEGIN EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40 IF @@ERROR<>0 OR @retcode<>0 GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_subscription_copy' BEGIN /* Update the allow_subscription_copy column */ UPDATE sysmergepublications SET allow_subscription_copy = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_synctoalternate' BEGIN /* Update the allow_synctoalternate column */ UPDATE sysmergepublications SET allow_synctoalternate = @value_bit WHERE pubid = @pubid if @@error <> 0 BEGIN GOTO UNDO END END END /* ** Update merge publication property at distributor side if necessaray */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description','allow_push', 'allow_pull', 'allow_anonymous','retention', 'allow_subscription_copy') BEGIN IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('allow_push', 'allow_pull', 'allow_anonymous','allow_subscription_copy') /* Translate values */ BEGIN IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' SELECT @value = '1' ELSE IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'false' SELECT @value = '0' END /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO UNDO END SELECT @dbname = DB_NAME() SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSchange_publication' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @dbname, @publication = @publication, @property = @property, @value = @value IF @@ERROR <> 0 OR @retcode <> 0 BEGIN GOTO UNDO END END COMMIT TRAN --update its registration in active directory if @in_ActiveD=1 and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description','allow_pull', 'allow_anonymous') begin create table #guid_name_for_ADupdate(ad_guidname sysname collate database_default null) if @@ERROR<>0 begin goto FAILURE end insert into #guid_name_for_ADupdate exec @retcode = master.dbo.sp_ActiveDirectory_Obj N'UPDATE', N'PUBLICATION', @publication, @db_name, @ad_guidname if @@ERROR<>0 or @retcode<>0 begin goto FAILURE end select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_ADupdate if @ad_guidname is not NULL begin update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid if @@ERROR<>0 goto FAILURE end drop table #guid_name_for_ADupdate end /* ** Return succeed. */ RAISERROR (14077, 10, -1) RETURN (0) FAILURE: drop table #guid_name_for_ADupdate raiserror(21371, 10, -1, @publication) return (1) UNDO: if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION changemergepublication COMMIT TRANSACTION end GO exec dbo.sp_MS_marksystemobject sp_changemergepublication go grant execute on dbo.sp_changemergepublication to public go raiserror('Creating procedure sp_helpmergepublication', 0,1) GO CREATE PROCEDURE sp_helpmergepublication ( @publication sysname = '%', /* The publication name */ @found int = NULL OUTPUT, @publication_id uniqueidentifier = NULL OUTPUT, @reserved nvarchar(20) = NULL ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @no_row bit declare @our_srvid int declare @has_subscription bit /* ** Initializations. */ select @has_subscription = 0 if @found is NULL BEGIN select @no_row=0 END else BEGIN select @no_row=1 END select @found = 0 select @our_srvid = max(srvid) from master.dbo.sysservers where UPPER(srvname) = UPPER(@@SERVERNAME) collate database_default /* ** Running sp_help is OK from everywhere, whether enabled for publishing or not */ IF not exists (select * from sysobjects where name='sysmergesubscriptions') RETURN (0) /* ** Parameter Check: @publication. ** Check to make sure that there are some publications ** to display. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) = 'internal' GOTO SelectPubs if NOT EXISTS (select * FROM sysmergepublications pub, sysmergesubscriptions sub WHERE pub.name like @publication and UPPER(pub.publisher)=UPPER(@@servername) and pub.publisher_db=db_name() and sub.pubid = pub.pubid and sub.srvid = @our_srvid and sub.db_name = db_name()) BEGIN select @found = 0 RETURN (0) END else BEGIN select @found = 1 select @publication_id = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if exists (select * from sysmergesubscriptions where pubid<>subid and pubid in (select pubid from sysmergepublications where name like @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())) select @has_subscription = 1 if @no_row <> 0 RETURN(0) END SelectPubs: CREATE TABLE #tab1 ( id int identity NOT NULL, name sysname collate database_default not null, description nvarchar(255) collate database_default null, status tinyint NOT NULL, retention int NULL, sync_mode tinyint NULL, allow_push int NOT NULL, allow_pull int NOT NULL, allow_anonymous int NOT NULL, centralized_conflicts int NOT NULL, priority float(8) NOT NULL, snapshot_ready tinyint NOT NULL, publication_type int NULL, pubid uniqueidentifier NOT NULL, snapshot_jobid binary(16) NULL, enabled_for_internet int NULL, dynamic_filters int NULL, has_subscription bit NULL, -- Portable snapshot support snapshot_in_defaultfolder bit default 1 NOT NULL, alt_snapshot_folder nvarchar(255) collate database_default null, -- Pre/post-snapshot commands pre_snapshot_script nvarchar(255) collate database_default null, post_snapshot_script nvarchar(255) collate database_default null, -- Snapshot compression compress_snapshot bit default 0 NOT NULL, -- Post 7.0 FTP support ftp_address sysname collate database_default null, ftp_port int NOT NULL, ftp_subdirectory nvarchar(255) collate database_default null, ftp_login sysname collate database_default null, conflict_retention int NULL, keep_partition_changes int NULL, allow_subscription_copy int NULL, allow_synctoalternate int NULL, validate_subscriber_info nvarchar(500) collate database_default null, backward_comp_level int not null default 10, --defaulted to 70 server publish_to_activedirectory bit null, max_concurrent_merge int NULL, max_concurrent_dynamic_snapshots int NOT NULL default 0 ) /* This is valid at all sites - used for decentralized conflicts */ IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) = 'internal' begin INSERT into #tab1(name, description, status, retention, sync_mode, allow_push, allow_pull, allow_anonymous, centralized_conflicts, priority, snapshot_ready, publication_type, pubid, snapshot_jobid, enabled_for_internet, dynamic_filters, snapshot_in_defaultfolder, alt_snapshot_folder, pre_snapshot_script, post_snapshot_script, compress_snapshot, ftp_address, ftp_port, ftp_subdirectory, ftp_login, conflict_retention, keep_partition_changes, allow_subscription_copy, allow_synctoalternate, validate_subscriber_info,backward_comp_level, publish_to_activedirectory, max_concurrent_merge, max_concurrent_dynamic_snapshots) select pubs.name, pubs.description, pubs.status, pubs.retention, pubs.sync_mode, pubs.allow_push, pubs.allow_pull, pubs.allow_anonymous, pubs.centralized_conflicts, subs.priority, pubs.snapshot_ready, pubs.publication_type, pubs.pubid, replinfo.snapshot_jobid, pubs.enabled_for_internet, pubs.dynamic_filters, pubs.snapshot_in_defaultfolder, pubs.alt_snapshot_folder, pubs.pre_snapshot_script, pubs.post_snapshot_script, pubs.compress_snapshot, pubs.ftp_address, pubs.ftp_port, pubs.ftp_subdirectory, pubs.ftp_login, pubs.conflict_retention, pubs.keep_before_values, pubs.allow_subscription_copy, pubs.allow_synctoalternate, pubs.validate_subscriber_info, pubs.backward_comp_level, case when pubs.ad_guidname is NULL then 0 else 1 end, pubs.max_concurrent_merge, pubs.max_concurrent_dynamic_snapshots FROM sysmergesubscriptions subs, sysmergepublications pubs, MSmerge_replinfo replinfo WHERE pubs.name LIKE @publication AND UPPER(pubs.publisher)=UPPER(@@servername) AND pubs.publisher_db=db_name() AND subs.subid = pubs.pubid AND replinfo.repid = pubs.pubid AND subs.subscriber_type = 1 ORDER BY name end /* This is valid only at publishers and republishers */ else begin INSERT into #tab1(name, description, status, retention, sync_mode, allow_push, allow_pull, allow_anonymous, centralized_conflicts, priority, snapshot_ready, publication_type, pubid, snapshot_jobid, enabled_for_internet, dynamic_filters, has_subscription, snapshot_in_defaultfolder, alt_snapshot_folder, pre_snapshot_script, post_snapshot_script, compress_snapshot, ftp_address, ftp_port, ftp_subdirectory, ftp_login, conflict_retention, keep_partition_changes, allow_subscription_copy, allow_synctoalternate, validate_subscriber_info, backward_comp_level, publish_to_activedirectory,max_concurrent_merge,max_concurrent_dynamic_snapshots) select pubs.name, pubs.description, pubs.status, pubs.retention, pubs.sync_mode, pubs.allow_push, pubs.allow_pull, pubs.allow_anonymous, pubs.centralized_conflicts, subs.priority, pubs.snapshot_ready, pubs.publication_type, pubs.pubid, replinfo.snapshot_jobid, pubs.enabled_for_internet, pubs.dynamic_filters, case when exists (select * from sysmergesubscriptions where pubid<>subid and pubid in (select in_pubs.pubid from sysmergepublications in_pubs where in_pubs.name = pubs.name and UPPER(in_pubs.publisher)=UPPER(@@servername) and in_pubs.publisher_db=db_name())) then 1 else 0 end, pubs.snapshot_in_defaultfolder, pubs.alt_snapshot_folder, pubs.pre_snapshot_script, pubs.post_snapshot_script, pubs.compress_snapshot, pubs.ftp_address, pubs.ftp_port, pubs.ftp_subdirectory, pubs.ftp_login, pubs.conflict_retention, pubs.keep_before_values, pubs.allow_subscription_copy, pubs.allow_synctoalternate, pubs.validate_subscriber_info, pubs.backward_comp_level, case when pubs.ad_guidname is NULL then 0 else 1 end , pubs.max_concurrent_merge, pubs.max_concurrent_dynamic_snapshots FROM sysmergesubscriptions subs, sysmergepublications pubs, MSmerge_replinfo replinfo WHERE pubs.name LIKE @publication and UPPER(pubs.publisher)=UPPER(@@servername) and pubs.publisher_db=db_name() AND subs.subid = pubs.pubid AND replinfo.repid = pubs.pubid AND subs.subscriber_type = 1 AND subs.srvid = @our_srvid AND subs.db_name = db_name() ORDER BY name end if @@ERROR <> 0 RETURN (1) update #tab1 set snapshot_ready=0 where snapshot_ready>1 if @@ERROR<>0 RETURN (1) select * FROM #tab1 RETURN (0) go exec dbo.sp_MS_marksystemobject sp_helpmergepublication go grant execute on dbo.sp_helpmergepublication to public go raiserror('Creating procedure sp_dropmergepublication', 0,1) GO CREATE PROCEDURE sp_dropmergepublication( @publication sysname, /* The publication name */ @ignore_distributor bit = 0, @reserved bit = 0 ) AS set nocount on /* ** Declarations. */ declare @ad_guidname sysname declare @pubid uniqueidentifier declare @article sysname declare @cmd nvarchar(255) declare @retcode int declare @distproc nvarchar(300) declare @distributor sysname declare @distribdb sysname declare @working_dir varchar(255) declare @working_dir_drive varchar(255) declare @pub_dir nvarchar(255) declare @db_name sysname declare @implicit_transaction int declare @close_cursor_at_commit int declare @dynamic_filters bit declare @alt_snapshot_folder nvarchar(255) declare @pub_alt_snapshot_folder nvarchar(255) select @close_cursor_at_commit = 0 select @implicit_transaction = 0 /* ** Save setting values first before changing them */ IF (@reserved = 0) BEGIN SELECT @implicit_transaction = @@options & 2 SELECT @close_cursor_at_commit = @@options & 4 SET IMPLICIT_TRANSACTIONS OFF SET CURSOR_CLOSE_ON_COMMIT OFF END set @ad_guidname = NULL /* ** Initializations. */ select @db_name = db_name() /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) if LOWER(@publication) = 'all' BEGIN declare hC1 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergepublications where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() FOR READ ONLY OPEN hC1 FETCH hC1 INTO @publication WHILE (@@fetch_status <> -1) BEGIN EXECUTE dbo.sp_dropmergepublication @publication=@publication, @ignore_distributor = @ignore_distributor, @reserved = 1 FETCH hC1 INTO @publication END CLOSE hC1 DEALLOCATE hC1 RETURN (0) END if @publication IS NULL BEGIN RAISERROR (14003, 16, -1) RETURN (1) END /* ** Get the @pubid. */ if NOT EXISTS (select * FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()) BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @pubid = pubid, @dynamic_filters = @dynamic_filters, @alt_snapshot_folder = alt_snapshot_folder FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() select @ad_guidname=ad_guidname FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() /* ** Ignore external publications */ if exists (select * from sysmergesubscriptions where subid=@pubid and pubid=@pubid and db_name<>db_name()) RETURN (0) /* ** Check to make sure that there are push or pull subscriptions on the publication. */ if EXISTS (select * FROM sysmergesubscriptions subs, sysmergepublications pubs, MSmerge_replinfo repinfo WHERE pubs.name = @publication AND UPPER(pubs.publisher)=UPPER(@@servername) AND pubs.publisher_db=db_name() AND subs.pubid = pubs.pubid AND subs.status <> 2 -- Having a deleted subscription row is fine AND repinfo.repid <> @pubid AND subs.subid <> subs.partnerid) BEGIN RAISERROR (14005, 16, -1) RETURN (1) END begin tran save TRANSACTION dropmergepublication /* ** delete on-demand user script, if any */ if exists (select * from sysmergeschemachange where schematype=46 and pubid=@pubid) begin exec @retcode=sp_MSremove_userscript @pubid, 1 --we may not want to do error checking here end /* ** Delete all articles from the publication. */ update sysmergepublications set snapshot_ready=0 where pubid=@pubid --so that articles can be dropped if @@ERROR<>0 goto FAILURE EXECUTE @retcode = dbo.sp_dropmergearticle @publication = @publication, @article = 'all', @ignore_distributor = @ignore_distributor if @@ERROR <> 0 OR @retcode <> 0 begin RAISERROR (20040, 16, -1, @publication) goto FAILURE end /* ** Delete sync task of Publication. */ execute @retcode = dbo.sp_MSdropmergepub_snapshot @publication = @publication, @ignore_distributor = @ignore_distributor if @@ERROR <> 0 OR @retcode <> 0 begin RAISERROR (20010, 16, -1, @publication) goto FAILURE end /* ** Remove my own subscription from sysmergesubscriptions. */ if exists (select * from sysmergesubscriptions where subid = @pubid) begin DELETE from sysmergesubscriptions WHERE subid = @pubid if @@ERROR <> 0 goto FAILURE end if exists (select * from MSmerge_replinfo where repid = @pubid) begin DELETE from MSmerge_replinfo WHERE repid = @pubid if @@ERROR <> 0 goto FAILURE end /* ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC. */ if @ignore_distributor = 0 begin /* ** Get distribution server information for remote RPC call. */ EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT, @directory = @working_dir OUTPUT IF @@ERROR <> 0 OR @retcode <> 0 BEGIN RAISERROR (14071, 16, -1) goto FAILURE END /* ** Drop the publication info from the distributor */ select @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSdrop_publication' EXECUTE @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @db_name, @publication = @publication if @@ERROR <> 0 OR @retcode <> 0 begin goto FAILURE end /* ** If alternate snapshot folder is specified for this publication, ** try to remove the publication's snapshot folder underneath the ** the alternate snapshot location in Distributor's context */ if @alt_snapshot_folder is not null and @alt_snapshot_folder <> N'' begin /* Append publication specific folder name */ if substring(@alt_snapshot_folder,len(@alt_snapshot_folder),1)<> N'\' begin select @alt_snapshot_folder = @alt_snapshot_folder + N'\' end -- UNC version select @pub_alt_snapshot_folder = @alt_snapshot_folder + N'unc\' + fn_replcomposepublicationsnapshotfolder(@@servername,@db_name,@publication) collate database_default select @distproc = fn_replquotename(RTRIM(@distributor)) collate database_default + '.' + fn_replquotename(@distribdb) collate database_default + '.dbo.sp_MSreplremoveuncdir' -- Ignore errors as the snapshot folder may not exist at all EXECUTE @distproc @dir = @pub_alt_snapshot_folder -- FTP-enabled version select @pub_alt_snapshot_folder = @alt_snapshot_folder + N'ftp\' + fn_replcomposepublicationsnapshotfolder(@@servername,@db_name,@publication) collate database_default select @distproc = fn_replquotename(RTRIM(@distributor)) collate database_default + '.' + fn_replquotename(@distribdb) collate database_default + '.dbo.sp_MSreplremoveuncdir' -- Ignore errors as the snapshot folder may not exist at all EXECUTE @distproc @dir = @pub_alt_snapshot_folder end end /* Remove all dynamic snapshot jobs of this publication */ exec @retcode = sp_MSdropmergedynamicsnapshotjob @publication = @publication, @ignore_distributor = @ignore_distributor if @@ERROR <> 0 OR @retcode <> 0 begin goto FAILURE end /* ** Execute the cleanup routine for the publication */ exec dbo.sp_MSpublicationcleanup @publisher=@@SERVERNAME, @publisher_db = @db_name, @publication = @publication if @@ERROR <> 0 goto FAILURE DECLARE @retval INT EXECUTE @retval = master.dbo.xp_MSADEnabled if @ad_guidname is not NULL begin if @retval = 0 begin exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'DELETE', 'PUBLICATION', @publication, @db_name, @ad_guidname if @@ERROR<>0 or @retcode<>0 begin raiserror(21369, 16, -1, @publication) goto FAILURE end end else begin RAISERROR(21254, 16, -1, @publication) GOTO FAILURE end end COMMIT TRANSACTION /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END return (0) FAILURE: RAISERROR (14006, 16, -1) /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION dropmergepublication COMMIT TRANSACTION end /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END RETURN (1) go exec dbo.sp_MS_marksystemobject sp_dropmergepublication go grant execute on dbo.sp_dropmergepublication to public go raiserror('Creating procedure sp_mergearticlecolumn', 0, 1) GO CREATE PROCEDURE sp_mergearticlecolumn ( @publication sysname, /* The publication name */ @article sysname, /* The article name */ @column sysname = NULL, /* The column name */ @operation nvarchar(4) = 'add', /* Add or delete a column */ @schema_replication nvarchar(5) = 'false', /* reserved for internal use */ @force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @mergepublish int declare @iscomputed int declare @xtype int declare @sync_mode int declare @index_cnt int declare @v_unique_constraint int declare @v_unique_index int declare @indid int declare @in_partition bit declare @colid int DECLARE @cnt int, @idx int /* Loop counter, index */ DECLARE @columnid smallint /* Columnid-1 = bit to set */ DECLARE @columns binary(128) /* Temporary storage for the converted column */ DECLARE @pubid uniqueidentifier /* Publication identification number */ DECLARE @retcode int /* Return code for stored procedures */ DECLARE @artid uniqueidentifier declare @object_view sysname declare @filter_clause nvarchar(1000) DECLARE @objid int /* Article base table id */ declare @tmp_artid uniqueidentifier declare @tmp_object sysname declare @publisher sysname declare @publisher_db sysname declare @pkkey sysname declare @conflict_table sysname declare @status_value int declare @column_list nvarchar(4000) declare @ins_conflict_proc sysname declare @qual_source_object nvarchar(270) declare @qual_object_view nvarchar(270) declare @qual_tmp_object nvarchar(270) declare @source_object nvarchar(300) declare @v_foreign_key int declare @quoted_source_object nvarchar(270) select @publisher = @@SERVERNAME select @publisher_db = db_name() /* ** Security Check */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) select @mergepublish = 0x4000 select @v_unique_index = 2 -- status in sysindexes select @v_foreign_key = 3 -- status in sysconstraints select @v_unique_constraint = 4096 --status in sysindexes /* ** Check to see if the database has been activated for publication. */ IF ( (SELECT category & 4 FROM master..sysdatabases WHERE name = DB_NAME() collate database_default) = 0 ) BEGIN RAISERROR (14013, 16, -1) RETURN (1) END /* ** Parameter Check: @publication. ** Make sure that the publication exists and that it conforms to the ** rules for identifiers. */ IF @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END EXECUTE @retcode = dbo.sp_validname @publication IF @retcode <> 0 or @@ERROR<>0 RETURN (1) SELECT @pubid = pubid, @sync_mode = sync_mode FROM sysmergepublications WHERE name = @publication and LOWER(publisher)=LOWER(@publisher) and publisher_db = @publisher_db IF @pubid IS NULL BEGIN RAISERROR (20026, 11, -1, @publication) RETURN (1) END /* ** Parameter Check: @article. ** Check to make sure that the article exists in the publication. */ IF @article IS NULL BEGIN RAISERROR (14043, 16, -1, '@article') RETURN (1) END EXECUTE @retcode = dbo.sp_validname @article IF @retcode <> 0 or @@ERROR<>0 RETURN (1) /* ** Make sure the article exists. */ SELECT @artid = artid FROM sysmergearticles WHERE pubid = @pubid AND name = @article IF @artid IS NULL BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END /* ** Parameter Check: @column. ** Check to make sure that the column exists and conforms to the rules ** for identifiers. */ IF @column IS NOT NULL BEGIN EXECUTE @retcode = dbo.sp_validname @column IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) END /* ** Parameter Check: @operation. ** The operation can be either 'add' or 'drop'. */ IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('add', 'drop') BEGIN RAISERROR (14019, 16, -1) RETURN (1) END /* ** column name can not be null for 'drop' operation. OK for 'add' operation */ IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS)='drop' and @column is NULL BEGIN RAISERROR(14043, 16, -1, '@column') RETURN (1) END /* ** Can not drop non-identity, non-timestamp, non-computed columns that are not nullable and have no default value */ SELECT @status_value=status, @objid = objid, @source_object = object_name(objid) FROM sysmergearticles WHERE artid = @artid select @colid=colid from syscolumns where id=@objid and name=@column if not exists (select * from syscolumns where id = @objid and name=@column and (isnullable=1 OR type_name(xtype)='timestamp' OR iscomputed=1)) and not exists (select * from sysconstraints where id=@objid and colid=@colid and status & 5 = 5) and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS)='drop' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' and ColumnProperty(@objid, @column, 'IsIdentity') <> 1 BEGIN RAISERROR(21165, 16, -1, @column) return (1) END if LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' begin select @indid = indid from sysindexes where id = @objid and (status & 2048) <> 0 /* PK index */ select @index_cnt = 1 while (@index_cnt <= 16) begin select @pkkey = INDEX_COL(@source_object, @indid, @index_cnt) if @pkkey is NULL break if @pkkey=@column begin raiserror(21250, 16, -1, @column) return (1) end select @index_cnt = @index_cnt + 1 end /* ** Check for unique index defined on this column - to disallow such a column from being dropped */ if exists (select * from sysindexes where id=@objid and (status & @v_unique_index = @v_unique_index or status & @v_unique_constraint = @v_unique_constraint)) begin declare @keys varbinary(816) declare @i int declare #check_unique CURSOR LOCAL FAST_FORWARD for select indid from sysindexes where id=@objid and (status & @v_unique_index = @v_unique_index or status & @v_unique_constraint = @v_unique_constraint) open #check_unique fetch #check_unique into @indid while (@@fetch_status<>-1) begin SELECT @i = 1 WHILE (@i <= 16) BEGIN SELECT @pkkey = INDEX_COL(@source_object, @indid, @i) if @pkkey is NULL break if @pkkey=@column BEGIN if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='true' raiserror(21265, 16, -1, @column, @source_object) else raiserror(21347, 16, -1, @column) close #check_unique deallocate #check_unique return (1) END select @i = @i + 1 END fetch #check_unique into @indid end close #check_unique deallocate #check_unique end /* ** Check for foreign key constraints */ if exists (select * from sysconstraints where status & @v_foreign_key=@v_foreign_key and id=@objid) begin if @column in (select name from syscolumns where id=@objid and colid in (select colid from sysconstraints where status & @v_foreign_key=@v_foreign_key and id=@objid)) begin raiserror(21513, 16, -1, @column) return (1) end end end begin tran save TRANSACTION articlecolumn /* ** Make sure that the column <columns> is not NULL - if NULL set to 0x00. */ SELECT @columns = columns FROM sysmergearticles WHERE artid = @artid and pubid=@pubid IF @columns IS NULL UPDATE sysmergearticles SET columns = 0x00 WHERE artid = @artid and pubid=@pubid /* ** If no columns are specified, or if NULL is specified, set all ** the bits in the 'columns' column so all columns will be included, */ IF @column IS NULL BEGIN SELECT @cnt = max(colid), @idx = 1 FROM syscolumns WHERE id = @objid SELECT @columns = NULL WHILE @idx <= @cnt BEGIN /* to make sure column holes will not be included */ if exists (select * from syscolumns where colid=@idx and id=@objid and (@sync_mode=0 OR (iscomputed<>1 and type_name(xtype) <>'timestamp'))) begin exec sp_MSsetbit @bm=@columns OUTPUT, @coltoadd=@idx, @toset = 1 if @@ERROR<>0 or @retcode<>0 goto FAILURE update syscolumns set colstat=colstat | @mergepublish where id=@objid and colid=@idx if @@ERROR<>0 goto FAILURE end SELECT @idx = @idx + 1 END UPDATE sysmergearticles SET columns = @columns WHERE name = @article AND pubid = @pubid if @sync_mode=1 and exists (select * from syscolumns where id=@objid and (iscomputed=1 or type_name(xtype)='timestamp')) UPDATE sysmergearticles SET vertical_partition = 1 WHERE name = @article AND pubid = @pubid END ELSE BEGIN /* if @column is NULL, meanning all columns are in, do not bump up version to Shiloh. */ if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' raiserror(21351, 10, -1, @publication) else raiserror(21352, 10, -1, @publication) exec @retcode = sp_MSBumpupCompLevel @pubid, 40 if @@ERROR<>0 or @retcode<>0 GOTO FAILURE SELECT @columnid = colid, @iscomputed=iscomputed, @xtype=xtype FROM syscolumns WHERE id = @objid AND name = @column IF ((@@error <> 0) OR (@columnid IS NULL)) BEGIN RAISERROR (21166, 16, -1, @column) GOTO FAILURE END /* ** for character mode publications, we do not allow adding computed column or timestamp columns ** into the vertical parititioning. */ if @sync_mode=1 and (@iscomputed = 1 or type_name(@xtype) ='timestamp') and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add' begin if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' begin raiserror(21269, 16, -1) GOTO FAILURE end else begin if @@TRANCOUNT >0 begin ROLLBACK TRANSACTION articlecolumn COMMIT TRAN end return (0) end end if ColumnProperty(@objid, @column, 'isrowguidcol') = 1 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' begin RAISERROR(21162, 16, -1) GOTO FAILURE end exec @in_partition = sp_MStestbit @bm=@columns, @coltotest=@columnid if @in_partition=1 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' begin RAISERROR(21335, 10, -1, @column) GOTO FAILURE end if @in_partition=0 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' begin RAISERROR(21336, 10, -1, @column) GOTO FAILURE end SELECT @columns = columns, @filter_clause=subset_filterclause, @ins_conflict_proc=ins_conflict_proc, @conflict_table=conflict_table FROM sysmergearticles WHERE name = @article AND pubid = @pubid IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add' begin exec @retcode = sp_MSsetbit @bm = @columns OUTPUT, @coltoadd=@columnid, @toset=1 if @@ERROR<>0 or @retcode<>0 GOTO FAILURE update syscolumns set colstat=colstat | @mergepublish where id=@objid and colid=@colid if @@ERROR<>0 goto FAILURE end ELSE begin exec @retcode = sp_MSsetbit @bm = @columns OUTPUT, @coltoadd=@columnid, @toset=0 if @@ERROR<>0 or @retcode<>0 GOTO FAILURE if @columns = 0x00 begin raiserror(21345, 16, -1) goto FAILURE end exec @retcode = sp_MSclearcolumnbit @pubid, @artid, @column if @@ERROR<>0 or @retcode<>0 goto FAILURE end /* ** Set vertical_partitioning flag so that publication view would be re-generated even ** if there is not subsetfilters nor join filters */ UPDATE sysmergearticles SET columns = @columns, vertical_partition=1 WHERE name = @article AND pubid = @pubid IF @@ERROR <> 0 BEGIN RAISERROR (14021, 16, -1) GOTO FAILURE END select @column_list = NULL /* ** check to see if that column can be dropped based on current article's filter clause ** and if the article is involved in any join_filter_clauses - to make sure the drop of ** one column does not cripple any such joins */ if ((@filter_clause is not NULL and @filter_clause <>'' ) or exists (select * from sysmergesubsetfilters where pubid=@pubid and (artid=@artid or join_articlename=@article))) and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' begin exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid if @@ERROR<>0 or @retcode<>0 GOTO FAILURE select @object_view='TEMP_VIEW_' + @source_object --@source_object is not quoted select @qual_object_view=quotename(@object_view) exec @retcode = sp_MSget_qualified_name @objid, @qual_source_object OUTPUT if @@ERROR<>0 or @retcode<>0 goto FAILURE select @quoted_source_object=QUOTENAME(@source_object) exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @qual_source_object) if @@ERROR<>0 GOTO FAILURE if @filter_clause is not NULL and @filter_clause <>'' begin exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_source_object + ' where ' + @filter_clause) if @@ERROR<>0 begin exec('drop view ' + @qual_object_view) raiserror(21256, 16, -1, @filter_clause, @source_object) GOTO FAILURE end end end /* ** Check to make sure dropping a column will not breaking any other articles that using current article as join_article */ if exists (select * from sysmergesubsetfilters where pubid=@pubid and (artid=@artid or join_articlename=@article)) and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' begin declare @join_articlename sysname declare per_article CURSOR LOCAL FAST_FORWARD FOR select artid, join_filterclause, join_articlename from sysmergesubsetfilters where pubid=@pubid and (join_articlename=@article or artid=@artid) for READ ONLY open per_article fetch per_article into @tmp_artid, @filter_clause, @join_articlename while (@@fetch_status<>-1) begin if @artid<>@tmp_artid select @tmp_object=object_name(objid) from sysmergearticles where pubid=@pubid and artid=@tmp_artid else select @tmp_object=object_name(objid) from sysmergearticles where pubid=@pubid and name=@join_articlename select @qual_tmp_object=QUOTENAME(@tmp_object) if @tmp_object is not NULl and @tmp_object<>'' begin exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_source_object + ', ' + @qual_tmp_object + ' where ' + @filter_clause) if @@ERROR<>0 begin close per_article deallocate per_article raiserror(21256, 16, -1, @filter_clause, @source_object) GOTO FAILURE end end fetch per_article into @tmp_artid, @filter_clause, @join_articlename end close per_article deallocate per_article end END /* ** if snapshot is ready, change it to obsolete to force another snapshot run. ** Note this is the third value of snapshot_ready. 0 for not ready, 1 for OK, 2 for obsolete */ IF EXISTS (SELECT * FROM sysmergepublications WHERE pubid=@pubid and snapshot_ready>0) and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false' BEGIN update sysmergearticles set status=1, conflict_table=NULL where pubid=@pubid and artid=@artid and status<>5 and status <>6 if @@ERROR<>0 goto FAILURE /* ** Force a re-generation of conflict table and its ins_proc */ if object_id(@ins_conflict_proc) is not NULL begin exec ('drop proc ' + @ins_conflict_proc) if @@ERROR<>0 goto FAILURE end if object_id(@conflict_table) is not NULL begin exec ('drop table ' + @conflict_table) if @@ERROR<>0 goto FAILURE end /* ** make sure we know we really want to do this. */ if @force_invalidate_snapshot = 0 begin raiserror(20607, 16, -1) goto FAILURE end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 goto FAILURE if @force_reinit_subscription = 0 and @status_value<>5 --5 is the value for new_inactive begin raiserror(20608, 16, -1) goto FAILURE end --do a global re if @force_reinit_subscription = 1 begin --global reinitialization will bump up backward-comp-level to SP2. exec @retcode = sp_MSreinitmergepublication @publication if @retcode<>0 or @@ERROR<>0 goto FAILURE end /* ** Even for vertical partitioning on new article - we do not need to bumpup backward-comp-level ** else begin --bump up the backward-comp-level so that only 80 subscribers can use it. exec @retcode = sp_MSBumpupCompLevel @pubid, 40 if @@ERROR<>0 or @retcode<>0 GOTO FAILURE end */ END COMMIT TRANSACTION if exists (select * from sysobjects where id = object_id(@qual_object_view)) begin exec ('drop view ' + @qual_object_view) end return (0) FAILURE: if @@TRANCOUNT >0 begin ROLLBACK TRANSACTION articlecolumn COMMIT TRAN end if exists (select * from sysobjects where id = object_id(@qual_object_view)) begin exec ('drop view ' + @qual_object_view) end return (1) go EXEC dbo.sp_MS_marksystemobject sp_mergearticlecolumn GO raiserror('Creating procedure sp_helpmergearticlecolumn', 0, 1) GO CREATE PROCEDURE sp_helpmergearticlecolumn ( @publication sysname, /* The publication name */ @article sysname /* The article name */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @colid int declare @colmax int declare @colname sysname declare @published bit declare @columns binary(128) declare @pubid uniqueidentifier declare @retcode int declare @objid int declare @publisher sysname declare @publisher_db sysname select @publisher = @@SERVERNAME select @publisher_db = db_name() /* ** Parameter Check: @article. ** The @article name must conform to the rules for identifiers. */ IF @article IS NULL BEGIN RAISERROR (14043, 16, -1, '@article') RETURN (1) END EXECUTE @retcode = dbo.sp_validname @article IF @retcode <> 0 or @@ERROR<>0 RETURN (1) /* ** Parameter Check: @publication. ** The @publication name must conform to the rules for identifiers. */ IF @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END EXECUTE @retcode = dbo.sp_validname @publication IF @retcode <> 0 or @@ERROR<>0 RETURN (1) SELECT @pubid = pubid FROM sysmergepublications WHERE name = @publication and LOWER(publisher)=LOWER(@publisher) and publisher_db = @publisher_db IF @pubid IS NULL BEGIN RAISERROR (20026, 11, -1, @publication) RETURN (1) END /* ** Parameter Check: @article, @publication. ** Check to make sure that the article exists in this publication. */ IF NOT EXISTS (SELECT * FROM sysmergearticles WHERE pubid = @pubid AND name = @article) BEGIN RAISERROR (20027, 11, -1, @article) RETURN (1) END SELECT @columns = columns, @objid=objid FROM sysmergearticles WHERE name = @article AND pubid = @pubid create table #tmp (column_id int, column_name sysname collate database_default, published bit) select TOP 1 @colid = colid from syscolumns where id = @objid order by colid ASC select TOP 1 @colmax = colid from syscolumns where id = @objid order by colid DESC while (@colid <= @colmax) begin if exists (select * from syscolumns where id = @objid and colid = @colid) begin select @colname = name from syscolumns where id=@objid and colid=@colid exec @retcode = sp_MStestbit @bm=@columns, @coltotest=@colid if @retcode<>0 select @published=1 else select @published=0 insert into #tmp values(@colid, @colname, @published) end select @colid=@colid + 1 end select * from #tmp drop table #tmp go EXEC dbo.sp_MS_marksystemobject sp_helpmergearticlecolumn GO grant execute on dbo.sp_helpmergearticlecolumn to public go raiserror('Creating procedure sp_MSreinitmergepublication', 0, 1) GO create procedure sp_MSreinitmergepublication @publication sysname, @upload_first bit = 0 AS declare @pubid uniqueidentifier declare @schematext nvarchar(4000) declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematype int declare @publisher sysname declare @retcode int declare @publisher_db sysname set nocount on /* ** Security Check. ** We use login_name stored in syssubscriptions to manage security */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) select @publisher=@@servername select @publisher_db = db_name() select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() raiserror(21353,10, -1,@publication) exec @retcode=sp_MSBumpupCompLevel @pubid, 30 --for 7.0 SP2, which has limited support for this if @@ERROR<>0 or @retcode<>0 return (1) select @schematext = 'exec sp_MSreinit_hub '+ QUOTENAME(@publisher) + ', ' + QUOTENAME(@publisher_db) + ', ' + QUOTENAME(@publication) + ', ' + convert(nvarchar, @upload_first) select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() if @upload_first = 0 set @schematype = 12 /* reinit_all */ else set @schematype = 14 /* reinit_with_upload*/ exec @retcode=sp_MSinsertschemachange @pubid, @schemaguid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 return (1) update MSmerge_replinfo set schemaversion=0, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL where repid in (select subid from sysmergesubscriptions where subid<>pubid and pubid=@pubid and subscription_type=0) if @@ERROR<>0 return (1) -- set all article status to inactive as a starting point - which sort or cleanup the newly added status -- 5 and 6. if @upload_first=0 --do not reset status is upload_first is true. Otherwise, --creating triggers would fail. begin update sysmergearticles set status=1 where pubid=@pubid if @@ERROR<>0 return (1) end update MSmerge_replinfo set schemaversion=-1, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL where repid in (select subid from sysmergesubscriptions where subid<>pubid and pubid=@pubid and subscription_type>0) if @@ERROR<>0 return (1) GO exec dbo.sp_MS_marksystemobject sp_MSreinitmergepublication go grant execute on dbo.sp_MSreinitmergepublication to public go raiserror('Creating procedure sp_MSreinit_hub', 0, 1) GO create procedure sp_MSreinit_hub @publisher sysname, @publisher_db sysname, @publication sysname, @upload_first bit AS declare @retcode int declare @pubid uniqueidentifier declare @hub_pubname sysname declare @hub_publisher sysname declare @hub_publisher_db sysname declare @hub_pubid uniqueidentifier declare @schematext nvarchar(4000) declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematype int /* ** Security Check. ** We use login_name stored in syssubscriptions to manage security */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db BEGIN TRAN SAVE TRAN reinithub /* Find all publications that are being republished at the subscriber */ declare reinit_hub CURSOR LOCAL FAST_FORWARD FOR select DISTINCT p.pubid, p.name, p.publisher, p.publisher_db FROM sysmergepublications p where UPPER(p.publisher)=UPPER(@@SERVERNAME) and p.publisher_db=db_name() and exists (select * from sysmergearticles where (objid in (select objid from sysmergearticles where pubid=@pubid)) and (objid in (select objid from sysmergearticles where pubid=p.pubid))) and p.pubid<>@pubid FOR READ ONLY open reinit_hub fetch reinit_hub into @hub_pubid, @hub_pubname, @hub_publisher, @hub_publisher_db while (@@fetch_status<>-1) begin if @upload_first=1 begin update sysmergesubscriptions set status=5 where pubid=@hub_pubid if @@ERROR<>0 goto FAILURE end select @schematext = 'exec sp_MSreinit_hub '+ QUOTENAME(@hub_publisher) + ', ' + QUOTENAME(@hub_publisher_db) + ', ' + QUOTENAME(@hub_pubname) + ', ' + convert(nvarchar, @upload_first) select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() if @upload_first=0 set @schematype = 12 /* reinit_all */ else set @schematype = 14 /*reinitwithupload */ exec @retcode=sp_MSinsertschemachange @hub_pubid, @schemaguid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 begin select @retcode = 1 GOTO FAILURE end raiserror(21354, 10, -1, @hub_pubname) exec @retcode=sp_MSBumpupCompLevel @hub_pubid, 40 if @@ERROR<>0 or @retcode<>0 GOTO FAILURE update sysmergepublications set snapshot_ready=2 where pubid=@hub_pubid if @@ERROR<>0 goto FAILURE fetch next from reinit_hub into @hub_pubid, @hub_pubname, @hub_publisher, @hub_publisher_db end close reinit_hub deallocate reinit_hub COMMIT TRAN return (0) FAILURE: close reinit_hub deallocate reinit_hub raiserror('Error occurred when applying reinit-all command at subscribers', 16, -1) if @@TRANCOUNT >0 begin ROLLBACK TRANSACTION reinithub COMMIT TRAN end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSreinit_hub go grant execute on dbo.sp_MSreinit_hub to public go raiserror('Creating procedure sp_reinitmergesubscription', 0, 1) GO create procedure sp_reinitmergesubscription @publication sysname = 'all', @subscriber sysname = 'all', @subscriber_db sysname = 'all', @upload_first nvarchar(5) = 'FALSE' AS declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @subscription_type int declare @reinit_bit int declare @publisher sysname declare @publisher_db sysname declare @distribdb sysname declare @distributor sysname declare @distproc nvarchar(300) declare @retcode int declare @uploadbit bit declare @pubname sysname declare @subsrvid int declare @subdb sysname declare @subname sysname /* ** Security Check. ** We use login_name stored in syssubscriptions to manage security */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) set nocount on /* ** Replace 'all' with '%' */ /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) if LOWER(@publication) = 'all' SELECT @publication = '%' if LOWER(@subscriber) = 'all' SELECT @subscriber = '%' if LOWER(@subscriber_db) = 'all' SELECT @subscriber_db = '%' select @publisher = @@SERVERNAME select @publisher_db=db_name() /* ** At publisher side, publication name is unique */ IF NOT EXISTS (SELECT * FROM sysmergepublications WHERE name LIKE @publication) BEGIN IF @publication = '%' RAISERROR (14008, 11, -1) ELSE RAISERROR (20026, 11, -1, @publication) RETURN (1) END /* if snapshot has not been ran yet, there is no point doing reinitialization */ if not exists (select * from sysmergepublications where name like @publication and snapshot_ready>0) return (0) if LOWER(@upload_first collate SQL_Latin1_General_CP1_CS_AS) = 'true' select @uploadbit = 1 else select @uploadbit = 0 EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSrefresh_anonymous ' if @subscriber = '%' and @subscriber_db = '%' begin exec @retcode = @distproc @publication, @publisher, @publisher_db if @@ERROR<>0 or @retcode<>0 return (1) declare reinit_all CURSOR LOCAL FAST_FORWARD FOR select name from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and snapshot_ready>0 and name like @publication For READ only open reinit_all fetch reinit_all into @pubname while (@@fetch_status<>-1) begin exec @retcode = sp_MSreinitmergepublication @pubname, @uploadbit if @@ERROR<>0 or @retcode<>0 begin close reinit_all deallocate reinit_all return (1) end fetch next from reinit_all into @pubname end close reinit_all deallocate reinit_all end SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSmarkreinit ' BEGIN TRAN SAVE TRAN reinitsubscription Declare SYN_CUR CURSOR LOCAL FAST_FORWARD FOR select subs.subid, subs.subscription_type, pubs.publisher, pubs.publisher_db, pubs.name, subs.srvid, subs.db_name from sysmergepublications pubs, sysmergesubscriptions subs where pubs.name LIKE @publication and UPPER(pubs.publisher)=UPPER(@@servername) and pubs.publisher_db=db_name() AND pubs.pubid=subs.pubid AND subs.pubid<>subs.subid AND subs.status <>0 --for unsynced subscription, there is no need to reinit. AND ((@subscriber_db = N'%') or (db_name = @subscriber_db)) AND srvid in (select srvid from master..sysservers where ((@subscriber = N'%') or (UPPER(srvname) = UPPER(@subscriber) collate database_default))) FOR READ ONLY open SYN_CUR fetch SYN_CUR into @subid, @subscription_type, @publisher, @publisher_db, @pubname, @subsrvid, @subdb while (@@fetch_status<>-1) BEGIN -- Security check if not exists (select * from sysmergesubscriptions subs1 where (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1) and subid = @subid) continue select @subname = srvname from master..sysservers where srvid = @subsrvid if @subscription_type = 0 update MSmerge_replinfo set schemaversion=0, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL where repid=@subid and schemaversion is NOT NULL else update MSmerge_replinfo set schemaversion= -1, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL where repid=@subid and schemaversion is NOT NULL if @@rowcount <> 0 and @uploadbit = 1 begin update sysmergesubscriptions set status = 5 where subid=@subid if @@ERROR<>0 goto Failure end -- If subscriber was preventing us from cleaning up metadata, set the status update sysmergesubscriptions set status = 8 where subid = @subid and status = 7 if @@rowcount <> 0 exec sp_MSquiescecheck -- 0 for push and -1 for pull exec @distproc @publisher, @publisher_db, @pubname, @subname, @subdb, 1 if @@ERROR<>0 BEGIN goto Failure END fetch next from SYN_CUR into @subid, @subscription_type, @publisher, @publisher_db, @pubname, @subsrvid, @subdb END close SYN_CUR deallocate SYN_CUR commit TRAN return (0) Failure: close SYN_CUR deallocate SYN_CUR if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION reinitsubscription COMMIT TRANSACTION end return (1) GO exec dbo.sp_MS_marksystemobject sp_reinitmergesubscription go grant execute on dbo.sp_reinitmergesubscription to public go raiserror('Creating procedure sp_MSpublicationview', 0,1) GO CREATE PROCEDURE sp_MSpublicationview( @publication sysname, @force_flag int = 0, @max_network_optimization bit = 0 ) AS declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @join_articlename nvarchar(270) declare @join_viewname nvarchar(270) declare @join_before_view nvarchar(270) declare @before_name nvarchar(270) declare @before_viewname nvarchar(270) declare @unqual_sourcename nvarchar(270) declare @article nvarchar(270) declare @art_nick int declare @join_nick int declare @join_filterclause nvarchar(4000) declare @bool_filterclause nvarchar(4000) declare @view_rule nvarchar(4000) declare @before_view_rule nvarchar(4000) declare @before_objid int declare @article_level int declare @progress int declare @art int declare @viewname nvarchar(270) declare @procname nvarchar(300) declare @source_objid int declare @source_object nvarchar(270) declare @sync_objid int declare @bitset int declare @permanent int declare @temporary int declare @filter_id int declare @filter_id_str nvarchar(10) declare @guidstr nvarchar(40) declare @pubidstr nvarchar(40) declare @rgcol nvarchar(270) declare @view_type int declare @belongsname nvarchar(270) declare @join_nickstr nvarchar(10) declare @unqual_jointable nvarchar(270) declare @retcode smallint declare @hasguid int declare @vertical_partition int declare @join_unique_key int declare @simple_join_view int declare @join_filterid int declare @allhaveguids int declare @command nvarchar(4000) declare @objid int declare @owner nvarchar(270) declare @table nvarchar(270) declare @quoted_view nvarchar(290) declare @before_rowguidname sysname declare @quoted_pub nvarchar(290) declare @quoted_proc nvarchar(290) declare @snapshot_ready int declare @columns varbinary(128) declare @column_list nvarchar(4000) declare @prefixed_column_list nvarchar(4000) declare @colname nvarchar(270) declare @colid int declare @dynamic_filters bit declare @alias_for_sourceobject sysname set @progress = 1 set @article_level = 0 set @permanent = 1 set @temporary = 2 set @allhaveguids = 1 set @before_rowguidname = NULL /* ** Only legal publisher can run this stored procedure */ set nocount on /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) select @pubid = pubid, @snapshot_ready = snapshot_ready, @dynamic_filters = dynamic_filters FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() IF @pubid IS NULL BEGIN RAISERROR (20026, 11, -1, @publication) RETURN (1) END select @table=object_name(objid) from sysmergearticles where pubid=@pubid and (columns is NULL or columns = 0x00) if @table is not NULL begin raiserror(21318, 16, -1, @table) return (1) end -- If snapshot is already ready, views are good. Don't drop and recreate as someone -- might be using them. if @snapshot_ready = 1 and @force_flag = 0 return (0) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <>0 return (1) create table #art(indexcol int identity NOT NULL, art_nick int NOT NULL, article_level int NOT NULL) if @@ERROR <> 0 begin goto FAILURE end while @progress > 0 BEGIN /* ** Select articles that have either a boolean_filter or at least one join filter ** into a temp table in an optimized order. */ insert into #art(art_nick, article_level) select nickname, @article_level from sysmergearticles where pubid=@pubid and nickname not in (select art_nick from #art) and nickname not in (select art_nickname from sysmergesubsetfilters where pubid=@pubid and join_nickname not in (select art_nick from #art)) /* ** NOTENOTE: add error checking here. */ set @progress = @@rowcount select @article_level = @article_level + 1 END /* Drop the old views and reset sync_objid */ select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid while @art_nick is not null begin /* Drop the old view */ select @viewname = OBJECT_NAME (sync_objid), @before_viewname = OBJECT_NAME(before_view_objid) from sysmergearticles where pubid = @pubid and nickname = @art_nick if @viewname IS NOT NULL begin select @quoted_view = QUOTENAME(@viewname) exec ('drop view ' + @quoted_view) end if @before_viewname IS NOT NULL begin exec ('drop view ' + @before_viewname) end /* Update the row in sysmergearticles */ update sysmergearticles set view_type = 0, sync_objid = objid where pubid = @pubid and nickname = @art_nick if @@ERROR <> 0 goto FAILURE /* Find the next one */ select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid end set @art = 0 select @art=min(indexcol) from #art where indexcol>@art while (@art is not null) begin select @art_nick=art_nick, @article_level = article_level from #art where indexcol = @art select @article = name, @artid = artid, @columns = columns, @source_objid = objid, @sync_objid = sync_objid, @procname = view_sel_proc, @before_objid = before_image_objid from sysmergearticles where nickname=@art_nick and pubid = @pubid exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @source_objid set @before_name = OBJECT_NAME(@before_objid) if @before_name is not null begin select @before_rowguidname=name from syscolumns where id=@source_objid and columnproperty(@source_objid, name , 'isrowguidcol')=1 exec @retcode = dbo.sp_MSguidtostr @pubid, @guidstr out set @before_viewname = @before_name + '_v_' + @guidstr end else set @before_viewname = NULL select @quoted_proc = QUOTENAME(@procname) exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out if @@ERROR <>0 OR @retcode <>0 return (1) select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects where id = @source_objid select @unqual_sourcename = QUOTENAME(OBJECT_NAME(@source_objid)) select @bool_filterclause=subset_filterclause, @vertical_partition=vertical_partition from sysmergearticles where name = @article and pubid = @pubid -- verify the syntax of boolean filter, if added with vertical-partition to true -- in this case, the filter clause can contain columns that do not exist in the partition. if len(@bool_filterclause) > 0 begin /* -- let server return appropriate error message exec ('select ' + @column_list + ' into #temptable_publicationview from ' + @source_object + 'declare @test int select @test=1 from #temptable_publicationview ' + @unqual_sourcename + ' where ' + @bool_filterclause) if @@ERROR<>0 begin raiserror(21256, 16, -1, @bool_filterclause, @source_object) return (1) end */ select @bool_filterclause = ' (' + @bool_filterclause + ') ' end set @rgcol = NULL select @rgcol = QUOTENAME(name) from syscolumns where id = @source_objid and ColumnProperty(id, name, 'isrowguidcol') = 1 if @rgcol is not NULL set @hasguid = 1 else begin set @hasguid = 0 set @allhaveguids = 0 end /* ** Process non looping articles that have either a boolean or a join_filter. */ if ( @article_level > 0 OR (len(@bool_filterclause) > 0) ) begin /* ** If the article has a previously generated view, then drop the view before ** creating the new one. */ set @viewname = NULL select @viewname = name from sysobjects where id = @sync_objid and ObjectProperty (id, 'IsView') = 1 and ObjectProperty (id, 'IsMSShipped') = 1 if @viewname IS NOT NULL begin select @quoted_view = QUOTENAME(@viewname) exec ('drop view ' + @quoted_view) if @@ERROR<>0 return (1) end /* ** Any join filter(s)? If any, process join filter(s) */ if (@article_level > 0) begin declare pub1 CURSOR LOCAL FAST_FORWARD FOR select join_filterclause, join_nickname, join_articlename, join_unique_key, join_filterid from sysmergesubsetfilters where pubid=@pubid and artid=@artid FOR READ ONLY open pub1 fetch pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid select @join_filterclause=' ( ' + @join_filterclause + ') ' select @unqual_jointable = QUOTENAME(name) from sysobjects where id = (select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) if @max_network_optimization = 0 select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid) from sysmergearticles where nickname = @join_nick and pubid = @pubid else select @join_viewname = object_name(sync_objid), @join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end) from sysmergearticles where nickname = @join_nick and pubid = @pubid select @join_viewname = QUOTENAME(@join_viewname) if (@join_unique_key = 1 and (@bool_filterclause is null or len(@bool_filterclause) = 0) and not exists (select * from sysmergesubsetfilters where pubid=@pubid and artid=@artid and join_filterid <> @join_filterid)) begin set @simple_join_view = 1 if @column_list = ' * ' select @column_list = ' ' + @unqual_sourcename + '.* ' set @view_rule = 'select ' + @column_list + ' from ' + @source_object + ' ' + @unqual_sourcename + ' , ' + @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause /* add security check to the view if this is a dynamically filtered publication */ set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end else begin set @simple_join_view = 0 /* Alias the source object with the unqualified name and use that to select the rowguidcol */ set @view_rule = 'select ' + @unqual_sourcename + '.rowguidcol from ' + @source_object + ' ' + @unqual_sourcename + ' , ' + @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause end if @before_name is not null set @before_view_rule = 'select * from ' + @before_name + ' ' + @unqual_sourcename + ' where exists (select * from ' + @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from ' + @join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') ' fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid WHILE (@@fetch_status <> -1) begin select @join_filterclause=' ( ' + @join_filterclause + ') ' select @unqual_jointable = name from sysobjects where id = ( select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) if @max_network_optimization = 0 select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid) from sysmergearticles where nickname = @join_nick and pubid = @pubid else select @join_viewname = object_name(sync_objid), @join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end) from sysmergearticles where nickname = @join_nick and pubid = @pubid select @join_viewname = QUOTENAME(@join_viewname) set @view_rule = @view_rule + ' union select ' + @source_object + '.rowguidcol from ' + @source_object + ', ' + @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause if @before_name is not null set @before_view_rule = @before_view_rule + ' or exists (select * from ' + @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from ' + @join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') ' fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid end close pub1 deallocate pub1 if len(@bool_filterclause) > 0 begin set @view_rule = @view_rule + ' union select ' + @source_object + '.rowguidcol from '+ @source_object + ' where '+ @bool_filterclause if @before_name is not null set @before_view_rule = @before_view_rule + ' or ' + @bool_filterclause end -- Now do the actual view rule as a semi-join, if not a simple join on unique key if (@simple_join_view = 0) begin /* ** Generate a unique alias for the outer select to make sure that it does not generate an ** ambiguous reference with table names used in the join_filter clause */ set @alias_for_sourceobject = 'alias_' + @guidstr exec @retcode = sp_MSgetcolumnlist @pubid, @prefixed_column_list OUTPUT, @source_objid, @alias_for_sourceobject set @view_rule = 'select ' + @prefixed_column_list + ' from ' + @source_object + ' ' + @alias_for_sourceobject + ' where rowguidcol in (' + @view_rule + ')' set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end end else /* boolean filter only */ begin select @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename + ' where '+ @bool_filterclause if @before_name is not null set @before_view_rule = ' select * from ' + @before_name + ' ' + @unqual_sourcename + ' where ' + @bool_filterclause set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end select @viewname = @publication + '_' + @article + '_VIEW' exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output select @quoted_view = QUOTENAME(@viewname) if @retcode <> 0 or @@ERROR <> 0 return (1) /* If we havent generated rowguidcol yet, use dummy rule that doesnt refer to it */ if @hasguid = 0 set @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename exec ('create view '+ @quoted_view + ' as '+ @view_rule) if @@ERROR<>0 return (1) /* grant select permission on sync view to public - security check is performed inside the view */ exec ('grant select on ' + @quoted_view + ' to public') if @@ERROR<>0 return (1) /* Mark view as system object */ execute sp_MS_marksystemobject @quoted_view if @@ERROR<>0 return (1) if @hasguid = 1 begin select @procname=view_sel_proc from sysmergearticles where pubid=@pubid and artid=@artid if object_id(@procname) is not NULL begin exec ('drop procedure ' + @quoted_proc) update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid end else begin set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output if @retcode <> 0 or @@ERROR <> 0 return (1) end select @owner = user_name(uid) from sysobjects where name = @viewname exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid if @retcode<>0 or @@ERROR<>0 return (1) update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid end select @quoted_view = QUOTENAME(@viewname) update sysmergearticles set sync_objid = OBJECT_ID (@quoted_view), view_type = @permanent where artid = @artid and pubid = @pubid if @before_name is not null and @before_view_rule is not null begin exec @retcode = sp_MScreatebeforetable @source_objid if @@ERROR <>0 OR @retcode <>0 return (1) if object_id(@before_viewname) is not NULL exec ('drop view ' + @before_viewname) exec ('create view ' + @before_viewname + ' as ' + @before_view_rule) if @@ERROR<>0 return (1) if @before_rowguidname is not NULL begin exec ('grant select (' + @before_rowguidname + ') on '+ @before_viewname + ' to public') if @@ERROR<>0 return (1) end exec ('grant select (generation) on '+ @before_viewname + ' to public') if @@ERROR<>0 return (1) execute sp_MS_marksystemobject @before_viewname if @@ERROR<>0 return (1) update sysmergearticles set before_view_objid = OBJECT_ID (@before_viewname) where artid = @artid and pubid = @pubid end end /* end of view creation for this article */ else begin select @sync_objid = @source_objid if @vertical_partition=1 and @column_list<> ' * ' begin select @viewname = @publication + '_' + @article + '_VIEW' exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output select @quoted_view = QUOTENAME(@viewname) set @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename set @view_rule = @view_rule + ' where ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' exec ('create view '+ @quoted_view + ' as '+ @view_rule) if @@ERROR<>0 return (1) execute sp_MS_marksystemobject @quoted_view if @@ERROR<>0 return (1) /* grant select permission on sync view to public - security check is performed inside the view */ exec ('grant select on ' + @quoted_view + ' to public') if @@ERROR<>0 return (1) select @sync_objid=object_id(@viewname) update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid where artid = @artid and pubid = @pubid end else if @dynamic_filters = 1 begin /* This article doesn't have any vertical or horizontal filters but if the publication is enabled for dynamic filtering, we still want to generate a dummy view so that logins in the publication access list can generate a dynamic snapshot. */ select @viewname = @publication + '_' + @article + '_VIEW' exec @retcode = dbo.sp_MSuniqueobjectname @viewname, @viewname output select @quoted_view = QUOTENAME(@viewname) set @view_rule = ' select * from ' + @source_object + ' where ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' exec ('create view '+ @quoted_view + ' as '+ @view_rule) if @@ERROR<>0 return (1) execute sp_MS_marksystemobject @quoted_view if @@ERROR<>0 return (1) exec ('grant select on ' + @quoted_view + ' to public') if @@ERROR<>0 return (1) select @sync_objid=object_id(@viewname) update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid, view_type = @permanent where artid = @artid and pubid = @pubid end if @hasguid = 1 begin /* still make the select proc, although it selects directly from table */ if object_id(@procname) is not NULL begin exec ('drop proc ' + @procname) update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid end set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output if @retcode <> 0 or @@ERROR <> 0 return (1) select @owner = user_name(uid), @viewname = name from sysobjects where id = @source_objid exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid end end select @art=min(indexcol) from #art where indexcol>@art end /* If there are looping articles, we must use a dynamic publication since no views on temp tables */ update sysmergearticles set view_type = @temporary where pubid=@pubid and nickname not in (select art_nick from #art) if @@rowcount > 0 begin if not exists (select * from sysmergepublications where dynamic_filters = 1 and pubid = @pubid) begin declare @repl_nick int /* treat these articles as if the publication were dynamic */ execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and view_type = @temporary while @art_nick is not null begin /* Loop over articles with circular filters. Create dummy view and add rows to contents */ select @article = name, @artid = artid, @source_objid = objid, @sync_objid = sync_objid, @procname = view_sel_proc from sysmergearticles where nickname=@art_nick and pubid = @pubid select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects where id = @source_objid set @viewname = NULL select @viewname = name from sysobjects where id = @sync_objid and ObjectProperty (id, 'IsView') = 1 and ObjectProperty (id, 'IsMSShipped') = 1 if @viewname IS NOT NULL begin select @quoted_view = QUOTENAME(@viewname) exec ('drop view ' + @quoted_view) if @@ERROR<>0 return (1) end select @viewname = 'SYNC_' + @publication + '_' + @article exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output if @retcode <> 0 or @@ERROR <> 0 return (1) select @quoted_view = QUOTENAME(@viewname) exec ('create view ' + @quoted_view + ' as select * from ' + @source_object + ' where 1 = 0 ') if @@ERROR<>0 return (1) update sysmergearticles set sync_objid = OBJECT_ID (@viewname), view_sel_proc = NULL where artid = @artid and pubid = @pubid if @@ERROR<>0 return (1) select @owner = user_name(uid) from sysobjects where id = @source_objid set @table = OBJECT_NAME(@source_objid) exec @retcode = dbo.sp_addtabletocontents @table, @owner IF @@ERROR <> 0 or @retcode <> 0 return (1) select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and view_type = @temporary and nickname > @art_nick end end end drop table #art if @allhaveguids = 1 begin declare @dbname sysname set @dbname = db_name() /* create the filter expand procs now */ set @filter_id = 0 select @filter_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and join_filterid > @filter_id while @filter_id is not null begin set @filter_id_str = convert(nvarchar(10), @filter_id) select @procname = expand_proc from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filter_id /* drop old proc, or generate a new procname */ select @quoted_proc = QUOTENAME(@procname) if object_id(@procname) is not NULL exec ('drop procedure ' + @quoted_proc) else begin set @procname = 'expand_' + @filter_id_str exec @retcode = dbo.sp_MSuniqueobjectname @procname, @procname output if @retcode <>0 return (1) update sysmergesubsetfilters set expand_proc = @procname where pubid = @pubid and join_filterid = @filter_id end select @quoted_proc = QUOTENAME(@procname) select @quoted_pub = QUOTENAME(@publication) set @command = 'exec dbo.sp_MSmakeexpandproc ' + @quoted_pub + ' , ' + @filter_id_str + ', ' + @quoted_proc exec @retcode = master..xp_execresultset @command, @dbname if @retcode <> 0 return (1) exec dbo.sp_MS_marksystemobject @quoted_proc if @@ERROR<>0 return (1) exec ('grant execute on ' + @quoted_proc + ' to public ') select @filter_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and join_filterid > @filter_id end end return (0) FAILURE: return (1) go exec dbo.sp_MS_marksystemobject sp_MSpublicationview go grant execute on dbo.sp_MSpublicationview to public go dump tran master with no_log go raiserror('Creating procedure sp_addmergesubscription', 0,1) GO CREATE PROCEDURE sp_addmergesubscription ( @publication sysname, /* Publication name */ @subscriber sysname = NULL, /* Subscriber server */ @subscriber_db sysname = NULL, /* Subscription database */ @subscription_type nvarchar(15) = 'push', /* Subscription type - push, pull */ @subscriber_type nvarchar(15) = 'local', /* Subscriber type */ @subscription_priority real = NULL, /* Subscription priority */ @sync_type nvarchar(15) = 'automatic', /* subscription sync type */ @frequency_type int = NULL, @frequency_interval int = NULL, @frequency_relative_interval int = NULL, @frequency_recurrence_factor int = NULL, @frequency_subday int = NULL, @frequency_subday_interval int = NULL, @active_start_time_of_day int = NULL, @active_end_time_of_day int = NULL, @active_start_date int = NULL, @active_end_date int = NULL, @optional_command_line nvarchar(4000) = NULL, @description nvarchar(255) = NULL, @enabled_for_syncmgr nvarchar(5) = 'false', /* Enabled for SYNCMGR: true or false */ -- Agent offload @offloadagent bit = 0, @offloadserver sysname = NULL, @use_interactive_resolver nvarchar(5) = 'false', @merge_job_name sysname = NULL ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @subnickname int declare @subscriber_srvid int declare @publisher_srvid int declare @priority real declare @subid uniqueidentifier declare @pubid uniqueidentifier /* Publication id */ declare @subscriber_typeid smallint declare @merge_jobid binary(16) /* Scheduler jobid for the merge agent */ declare @subscription_type_id int declare @distproc nvarchar(300) declare @command nvarchar(255) declare @inactive tinyint declare @subscriber_bit smallint declare @global tinyint /* subscriber type is global */ declare @push tinyint /* subscription type is push */ declare @partnerid uniqueidentifier /* Partner replica identifier */ declare @sync_typeid tinyint declare @nosync tinyint declare @automatic tinyint declare @distributor sysname declare @distribdb sysname declare @publisher sysname declare @publisher_db sysname declare @found int declare @datasource_type int declare @datasource_path sysname DECLARE @platform_nt binary declare @datasrctype int declare @datasrcpath sysname declare @use_interactive_bit bit /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Initializations. */ set @datasource_type = 0 /* Default SQL Server */ set @datasource_path = NULL set @datasrctype = 0 /* Default SQL Server */ set @datasrcpath = NULL set @platform_nt = 0x1 SET @nosync = 2 /* Const: synchronization type 'none' */ SET @automatic = 1 /* Const: synchronization type 'automatic' */ set @inactive = 0 SET @subscriber_bit = 4 set @global = 1 set @push = 0 set @pubid = NULL set @publisher = @@SERVERNAME set @publisher_db = DB_NAME() select @found = 1 /* Any non-NULL value is fine */ /* ** Parameter Check: @subscription_type. ** Set subscriber_typeid based on the @subscription_type specified. ** ** subscription_type subscription_type ** ================= =============== ** 0 push ** 1 pull */ if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('push', 'pull') BEGIN RAISERROR (14128, 16, -1) RETURN (1) END IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' set @subscription_type_id = 0 else set @subscription_type_id = 1 /* ** Parameter check: @offloadagent ** If @offloadagent = 1 then @subscription_type_id must be 0 */ IF (@offloadagent = 1) AND (@subscription_type <> N'push') BEGIN RAISERROR(21138, 16, -1) RETURN (1) END /* ** Security Check. */ IF @subscription_type_id = 0 BEGIN exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) END ELSE BEGIN exec @retcode = dbo.sp_MSreplcheck_pull @publication if @@ERROR <> 0 or @retcode <> 0 begin return(1) end END /* ** Validate that the publisher is a valid server */ select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default IF @publisher_srvid IS NULL BEGIN RAISERROR (14010, 16, -1) RETURN (1) END /* ** Parameter Check: @subscriber ** Check to make sure that the subscriber is defined */ IF @subscriber IS NULL BEGIN RAISERROR (14043, 16, -1, '@subscriber') RETURN (1) END IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default AND (srvstatus & @subscriber_bit) <> 0) BEGIN RAISERROR (14010, 16, -1) RETURN (1) END IF @subscriber = 'all' BEGIN RAISERROR (14136, 16, -1) RETURN (1) END /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO FAILURE END SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MShelp_subscriber_info ' exec @distproc @publisher, @subscriber, @found output if (@found <> 1) BEGIN RAISERROR (14085, 16, -1) RETURN (1) END exec @retcode = dbo.sp_MSget_subtypedatasrc @subscriber, @distributor, @distribdb, @datasrctype OUTPUT, @datasrcpath OUTPUT if @retcode<>0 return (1) if (@datasrctype = 2) or (@datasrctype = 4) or (@datasrctype = 5) BEGIN select @datasource_type = @datasrctype select @datasource_path = @datasrcpath END EXECUTE @retcode = dbo.sp_validname @subscriber IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) /* ** Parameter Check: @subscriber_db */ IF @subscriber_db IS NULL BEGIN RAISERROR (14043, 16, -1, '@subscriber_db') RETURN (1) END IF @subscriber_db = 'all' BEGIN RAISERROR (14136, 16, -1) RETURN (1) END /* ** Check to see if system tables exist. If not create them. Since under current ** design every database is qualified for subscribing. */ IF not exists (select name from sysobjects where name='sysmergesubscriptions') BEGIN execute @retcode = dbo.sp_MScreate_mergesystables if @@ERROR <> 0 or @retcode <> 0 begin return (1) end END /* ** Parameter Check: @publication. ** Check to make sure that the publication exists and that it conforms ** to the rules for identifiers. */ if NOT EXISTS (select * FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()) BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END if @pubid IS NULL select @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @partnerid IS NULL begin select @partnerid = subid FROM sysmergesubscriptions WHERE srvid = @publisher_srvid and db_name = @publisher_db and pubid = @pubid end /* ** Parameter Check: @subscriber_type. ** Set subscriber_typeid based on the @subscriber_type specified. ** ** subscriber_type subscriber_type ** ================= =============== ** 1 global ** 2 local ** 3 anonymous ** Type 'republisher' is taken out for B3. We may want to add this back later. */ if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('local', 'global') BEGIN RAISERROR (20037, 16, -1) RETURN (1) END if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('global') set @subscriber_typeid = 1 else if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('local') set @subscriber_typeid = 2 /* ** Parameter Check: @use_interactive_resolver */ if LOWER(@use_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, '@use_interactive_resolver') RETURN (1) END if LOWER(@use_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) = 'true' set @use_interactive_bit = 1 else set @use_interactive_bit = 0 /* ** Assign priority appropriately - choose 0.99 times the minimum priority ** of the global replicas. */ if (@subscription_priority >= 100.0 or @subscription_priority < 0.0) BEGIN RAISERROR (20088, 16, -1) RETURN (1) END if (@subscription_priority IS NULL) begin select @priority = 0.99 * min(priority) from sysmergesubscriptions where subscriber_type = 1 if (@priority IS NOT NULL) select @subscription_priority = @priority if (@subscription_priority IS NULL) select @subscription_priority = 0.0 end /* ** For local and anonymous subscriptions the priority is 0.0 */ if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('local', 'anonymous') select @subscription_priority = 0.0 /* ** Validate that the subscriber is a valid server */ select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default IF @subscriber_srvid IS NULL BEGIN RAISERROR (14010, 16, -1) RETURN (1) END IF exists (select * from sysobjects where name= 'syssubscriptions') begin if exists (select name from sysmergeextendedarticlesview where pubid=@pubid and objid in (select objid from sysextendedarticlesview where artid in (select artid from syssubscriptions where dest_db=@subscriber_db and srvid=@subscriber_srvid))) begin RAISERROR(21280, 16, -1, @publication, @subscriber_db) RETURN (1) end end /* ** Make sure that @offloadserver doesn't contain invalid characters */ exec @retcode = sp_MSreplcheckoffloadserver @offloadserver if @retcode<>0 or @@error<>0 return (1) /* ** Making it possible for a deleted subscription to come back. ** UNDONE : This disallows second pull subscription from being added unless the previous ** subscription was initial synced. */ if EXISTS (select db_name, srvid FROM sysmergesubscriptions WHERE db_name = @subscriber_db AND srvid = @subscriber_srvid AND pubid = @pubid AND status <>2) --We can definitely add back subscriptions that were deleted. BEGIN RAISERROR (14058, 16, -1) RETURN (1) END IF EXISTS (select db_name, srvid FROM sysmergesubscriptions WHERE db_name = @subscriber_db AND srvid = @subscriber_srvid AND pubid = @pubid AND status = 2) BEGIN select @subid = subid from sysmergesubscriptions WHERE db_name = @subscriber_db AND srvid = @subscriber_srvid AND pubid = @pubid delete from sysmergesubscriptions where subid = @subid delete from MSmerge_replinfo where repid = @subid END select @subid = newid() /* ** Parameter Check: @sync_type. ** Set sync_typeid based on the @sync_type specified. ** ** sync_typeid sync_type ** =========== ========= ** 1 automatic ** 2 none */ IF LOWER(@sync_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('automatic', 'none') BEGIN RAISERROR (14052, 16, -1) RETURN (1) END /* ** If current publication contains an article without rowguidcol, do not allow no-sync subscription */ IF LOWER(@sync_type collate SQL_Latin1_General_CP1_CS_AS) = 'automatic' BEGIN SET @sync_typeid = @automatic END ELSE BEGIN if exists (select * from sysmergearticles a where pubid=@pubid and not exists (select * from syscolumns c where c.id = a.objid and ColumnProperty(c.id, c.name, 'IsRowGuidCol') = 1)) BEGIN Raiserror(20086, 16, -1, @publication) RETURN (1) END else SET @sync_typeid = @nosync END /* ** UNDONE: Validate that the publisher is of type "republisher" */ begin tran save TRAN addmergesubscription /* Generate a guid for the Subscriber ID */ /* Look for existing nickname from any other subscription */ exec @retcode = dbo.sp_MSgetreplnick @subscriber, @subscriber_db , NULL, @subnickname out if (@@error <> 0) or @retcode <> 0 GOTO FAILURE /* Generate a new replica nickname from the @subid */ if (@subnickname is null) begin EXECUTE dbo.sp_MSgenreplnickname @subid, @subnickname output if @@ERROR<>0 GOTO FAILURE end /* ** The subscription doesn't exist, so let's add it to sysmergesubscriptions */ INSERT sysmergesubscriptions (subid, partnerid, datasource_type, datasource_path, srvid, db_name, pubid, status, subscriber_type, subscription_type, priority, sync_type, description, login_name, subscriber_server, use_interactive_resolver, publication, distributor) VALUES (@subid, @partnerid, @datasource_type, @datasource_path, @subscriber_srvid, @subscriber_db, @pubid, @inactive, @subscriber_typeid, @subscription_type_id, @subscription_priority, @sync_typeid, @description, suser_sname(suser_sid()), @subscriber, @use_interactive_bit, @publication, @distributor) if @@ERROR <> 0 BEGIN GOTO FAILURE END /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO FAILURE END SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadd_merge_subscription' EXEC @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @publisher_db, @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = @subscription_type_id, @sync_type = @sync_typeid, @status = @inactive, @frequency_type = @frequency_type, @frequency_interval = @frequency_interval, @frequency_relative_interval = @frequency_relative_interval, @frequency_recurrence_factor = @frequency_recurrence_factor, @frequency_subday = @frequency_subday, @frequency_subday_interval = @frequency_subday_interval, @active_start_time_of_day = @active_start_time_of_day, @active_end_time_of_day = @active_end_time_of_day, @active_start_date = @active_start_date, @active_end_date = @active_end_date, @optional_command_line = @optional_command_line, @merge_jobid = @merge_jobid OUTPUT, @offloadagent = @offloadagent, @offloadserver = @offloadserver, @agent_name = @merge_job_name IF @@ERROR <> 0 OR @retcode <> 0 begin goto FAILURE end /* ** Add row for subscription in MSmerge_replinfo. */ insert MSmerge_replinfo(repid, replnickname, merge_jobid) values (@subid, @subnickname, @merge_jobid) if @@ERROR <> 0 BEGIN GOTO FAILURE END /* Conditional support for MobileSync */ if LOWER(@enabled_for_syncmgr collate SQL_Latin1_General_CP1_CS_AS) = 'true' BEGIN /* MobileSync Support */ declare @distributor_server sysname declare @distributor_security_mode int declare @distributor_login sysname declare @distributor_password nvarchar(524) /* ** The registry entry needs to be created only for push subscriptions - ** i.e - need not be called when a pull subscription is created at the ** subscriber and sp_addmergesubscription is being called then. */ IF @subscription_type_id = 0 BEGIN EXECUTE @retcode = dbo.sp_helpdistributor @distributor = @distributor_server OUTPUT /* Distributor RPC server name */ IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO FAILURE END -- Always use integrated security on winNT if (@platform_nt = platform() & @platform_nt ) begin set @distributor_security_mode = 1 end -- For Win9x the dist publisher and distributor are the same machine else begin select @distributor_security_mode = 0, @distributor_login = login, @distributor_password = password from msdb..MSdistpublishers where UPPER(name) = UPPER(@@servername) collate database_default end /* Call sp_MSregistersubscription so that the subscription can be synchronized via Onestop etc. */ exec @retcode = dbo.sp_MSregistersubscription @replication_type = 2, @publisher = @@SERVERNAME, @publisher_db = @publisher_db, @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @distributor = @distributor, @distributor_security_mode = @distributor_security_mode, @distributor_login = @distributor_login, @distributor_password = @distributor_password, @subscription_id = @subid, @subscription_type = @subscription_type_id, @use_interactive_resolver = @use_interactive_bit IF @@error <> 0 OR @retcode <> 0 BEGIN GOTO FAILURE END END END COMMIT TRAN return (0) FAILURE: RAISERROR (14057, 16, -1) /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION addmergesubscription COMMIT TRANSACTION end RETURN (1) go exec dbo.sp_MS_marksystemobject sp_addmergesubscription go grant execute on dbo.sp_addmergesubscription to public go raiserror('Creating procedure sp_MSretrive_mergepublication', 0,1) GO CREATE PROCEDURE sp_MSretrieve_mergepublication @publication sysname AS declare @retcode int /* ** Security Check */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) select 'name' = name, 'database ' = db_name(), 'publisher' = @@SERVERNAME, 'type' = 'Merge', 'description ' = description, 'status ' = status, 'allow known pull subscription' = allow_pull, 'allow immediate-updating subscription ' = 0, 'allow anonymous ' = allow_anonymous, 'allow queued-updating subscription ' = 0, 'allow snapshot files FTP downloading' = enabled_for_internet, 'third party' = sync_mode from sysmergepublications where name = @publication go EXEC dbo.sp_MS_marksystemobject sp_MSretrieve_mergepublication GO raiserror('Creating procedure sp_changemergesubscription', 0,1) GO CREATE PROCEDURE sp_changemergesubscription ( @publication sysname = NULL, /* Publication name */ @subscriber sysname = NULL, /* Subscriber server */ @subscriber_db sysname = NULL, /* Subscription database */ @property sysname = NULL, /* The property to change */ @value nvarchar(255) = NULL, /* The new property value */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @subscriber_bit smallint declare @subscriber_srvid int declare @publisher_srvid int declare @retcode int declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @partnerid uniqueidentifier declare @sync_typeid tinyint declare @nosync tinyint declare @automatic tinyint declare @artid uniqueidentifier declare @schematype int declare @schemaversion int declare @schemaguid uniqueidentifier declare @db_name sysname declare @subscriber_type int declare @schematext nvarchar(2000) declare @publisher sysname declare @publisher_db sysname declare @use_interactive_bit bit declare @enabled_for_syncmgr int declare @regkey nvarchar(1000) declare @snapshot_ready int /* ** Initializations. */ SET @nosync = 2 /* Const: synchronization type 'none' */ SET @automatic = 1 /* Const: synchronization type 'automatic' */ set @publisher = @@SERVERNAME set @publisher_db = DB_NAME() /* ** Security Check. */ BEGIN exec @retcode = dbo.sp_MSreplcheck_subscribe if @@ERROR <> 0 or @retcode <> 0 return(1) END /* ** Check to see if current database is doing publishing/subscribing */ IF not exists (select name from sysobjects where name='sysmergesubscriptions') BEGIN RAISERROR (14055, 16, -1) RETURN (1) END /* ** Parameter Check: @property. ** If the @property parameter is NULL, print the options. */ IF @property IS NULL BEGIN CREATE TABLE #tab1 (properties sysname collate database_default) INSERT INTO #tab1 VALUES ('sync_type') INSERT INTO #tab1 VALUES ('priority') INSERT INTO #tab1 VALUES ('description') INSERT INTO #tab1 VALUES ('use_interactive_resolver') select * FROM #tab1 RETURN (0) END /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ IF @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END select @pubid = pubid, @snapshot_ready=snapshot_ready FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() IF @pubid IS NULL BEGIN RAISERROR (20026, 11, -1, @publication) RETURN (1) END /* ** Validate that the publisher is a valid server */ select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default IF @publisher_srvid IS NULL BEGIN RAISERROR (14010, 16, -1) RETURN (1) END /* ** Parameter Check: @subscriber. ** Check to make sure we have a valid subscriber. */ IF @subscriber IS NULL BEGIN RAISERROR (14043, 16, -1, '@subscriber') RETURN (1) END /* ** Validate that the subscriber is a valid server */ select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default IF @subscriber_srvid IS NULL BEGIN RAISERROR (14010, 16, -1) RETURN (1) END /* ** Check to see if you have a local / global subscription on this publication */ set @subid = NULL select @subid = subs1.subid, @pubid = pubs.pubid, /* identified from publication name */ @subscriber_type=subs1.subscriber_type, @partnerid = subs2.subid from sysmergesubscriptions subs1, sysmergesubscriptions subs2, sysmergepublications pubs where subs1.srvid = @subscriber_srvid and subs1.db_name = @subscriber_db and subs2.srvid = @publisher_srvid and subs2.db_name = @publisher_db and subs1.pubid = subs2.subid and subs2.pubid = pubs.pubid and pubs.name = @publication and UPPER(pubs.publisher)=UPPER(@@servername) and pubs.publisher_db=db_name() if @subid IS NULL begin RAISERROR (14050, 11, -1) RETURN(1) end /* ** Parameter Check: @property. ** Check to make sure that @property is a valid property in ** sysarticles. */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('sync_type', 'priority', 'description', 'use_interactive_resolver') BEGIN RAISERROR (20078, 16, -1) RETURN (1) END BEGIN TRANSACTION change_subscription save TRAN change_subscription IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'use_interactive_resolver' BEGIN /* Check to make sure that we have a true/false. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false') BEGIN RAISERROR (14148, 16, -1, 'use_interactive_resolver') goto UNDO END /* Determine the bit value. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' SET @use_interactive_bit = 1 ELSE SET @use_interactive_bit = 0 /* Update the subscription with the new 'use_interactive' value. */ UPDATE sysmergesubscriptions SET use_interactive_resolver = @use_interactive_bit WHERE subid = @subid IF @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END /* If the subscription is enable for Sync Manager, then update the reg value */ exec sp_MSsubscription_enabled_for_syncmgr @publisher, @publisher_db, @publication, @subscriber, @subscriber_db, @enabled_for_syncmgr OUT, @regkey OUT IF @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END if @enabled_for_syncmgr = 1 begin EXECUTE @retcode = master.dbo.xp_regwrite 'HKEY_LOCAL_MACHINE', @regkey, 'UseInteractiveResolver', 'REG_DWORD', @use_interactive_bit if @retcode <> 0 OR @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END end END /* ** Change the property. */ IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'sync_type' BEGIN /* ** Check to make sure that we have a valid sync_type. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('automatic', 'none') BEGIN RAISERROR (14052, 16, -1) goto UNDO END /* ** Determine the integer value for the sync_type. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'automatic' SET @sync_typeid = @automatic ELSE BEGIN /* ** If current publication contains an article without rowguidcol, do not allow no-sync subscription */ if exists (select * from sysmergearticles a where pubid = @pubid and not exists (select * from syscolumns c where c.id=a.objid and columnproperty (c.id, c.name, 'isrowguidcol')=1)) begin Raiserror(20086, 16, -1, @publication) goto UNDO end else SET @sync_typeid = @nosync END /* ** Update the subscription with the new sync_type. */ UPDATE sysmergesubscriptions SET sync_type = @sync_typeid WHERE subid = @subid IF @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END if @snapshot_ready>0 begin --if there is one article in this publication with pre_command of other than 'drop', --do not allow changing subscription property. if exists (select * from sysmergearticles where pubid=@pubid and pre_creation_command<>1) begin RAISERROR (21420, 16, -1, @property) goto UNDO end /* ** make sure we know we really want to do this. */ if @force_reinit_subscription = 0 begin raiserror(20608, 16, -1) goto UNDO end exec @retcode=sp_MSreinitmergepublication @publication if @@ERROR<>0 or @retcode<>0 GOTO UNDO end END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'description' BEGIN UPDATE sysmergesubscriptions SET description = @value WHERE subid = @subid IF @@ERROR <> 0 BEGIN RAISERROR (14053, 16, -1) goto UNDO END END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'priority' BEGIN select @db_name = db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> db_name() BEGIN RAISERROR (20047, 16, -1) goto UNDO END /* Only the original publisher can change priority of a global subscriptions */ IF @subscriber_type<>1 BEGIN RAISERROR (20044, 16, -1) /* Local subscriber does not have priority*/ goto UNDO END IF convert(real, @value)>100.0 BEGIN RAISERROR (20049, 16, -1) /* Don't accept priority greater than 100 */ goto UNDO END exec dbo.sp_MSchange_priority @subid, @value if @@ERROR<>0 goto UNDO /* Insert the sp_MSchange_priority schema change only if the publication's snapshot is ready */ if (@snapshot_ready > 0) begin select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @artid = newid() set @schematype = 8 /* change priority */ select @schematext = 'exec dbo.sp_MSchange_priority '+ '''' + convert(nchar(36),@subid) + '''' + ',' + '''' + @value + '''' exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 goto UNDO end END COMMIT TRANSACTION /* ** Return succeed. It is not an error message. */ RAISERROR (14054, 10, -1) RETURN (0) UNDO: if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION change_subscription COMMIT TRANSACTION end return (1) go exec dbo.sp_MS_marksystemobject sp_changemergesubscription go grant execute on dbo.sp_changemergesubscription to public go raiserror('Creating procedure sp_helpmergesubscription', 0,1) GO CREATE PROCEDURE sp_helpmergesubscription( @publication sysname = '%', /* Publication name */ @subscriber sysname = '%', /* Subscriber server */ @subscriber_db sysname = '%', /* Subscription database */ @publisher sysname = '%', /* Publisher server */ @publisher_db sysname = '%', /* Publisher database */ @subscription_type nvarchar(15) = 'both', /* Subscription type - push or pull */ @found int = NULL OUTPUT )AS SET NOCOUNT ON /* ** Declarations. */ declare @db sysname declare @retcode int declare @subscriber_bit smallint declare @srvid int declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @partnerid uniqueidentifier declare @cursor_open int declare @no_row bit declare @subscription_type_id int declare @distributor sysname declare @distributiondb sysname declare @distproc nvarchar(300) declare @dbname sysname select @distributor = null select @distributiondb = null select @distproc = null select @dbname = null /* ** Initializations. */ set @subscriber_bit = 4 set @cursor_open = 0 /* ** Initializations of @now_row. */ IF @found is NULL BEGIN SELECT @no_row=0 END ELSE BEGIN SELECT @no_row=1 END select @db=db_name() -- so that it can appear in dynamic query /* ** Calling sp_help* is all right whether current database is enabled for pub/sub or not */ IF not exists (select * from sysobjects where name='sysmergesubscriptions') RETURN (0) /* Security check */ EXEC @retcode = dbo.sp_MSreplcheck_pull @publication = @publication, @raise_fatal_error = 0 if @@ERROR <> 0 or @retcode <> 0 return(1) /* ** Parameter Check: @subscription_type. ** Set subscription_typeid based on the @subscription_type specified. ** ** subscription_type subscription_type ** ================= =============== ** 0 push ** 1 pull ** 2 both */ if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('push', 'pull', 'both') BEGIN RAISERROR (20079, 16, -1) RETURN (1) END IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both' set @subscription_type_id = 2 else IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' set @subscription_type_id = 0 else set @subscription_type_id = 1 /* ** Parameter Check: @publisher ** Check to make sure that the publisher is defined */ IF @publisher <> '%' BEGIN EXECUTE @retcode = dbo.sp_validname @publisher IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) END /* ** Parameter Check: @subscriber. ** If remote server, limit the view to the remote server's subscriptions. ** Make sure that the name isn't NULL. */ if @subscriber IS NULL BEGIN RAISERROR (14043, 16, -1, '@subscriber') RETURN (1) END /* ** Parameter Check: @subscriber. ** Check if remote server is defined as a subscription server, and ** that the name conforms to the rules for identifiers. */ if @subscriber <> '%' BEGIN EXECUTE @retcode = dbo.sp_validname @subscriber if @retcode <> 0 OR @@ERROR <> 0 RETURN (1) if NOT EXISTS (select * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default AND (srvstatus & @subscriber_bit) <> 0) BEGIN --RAISERROR (14010, 16, -1) RETURN (1) END END /* ** Parameter Check: @publication. ** If the publication name is specified, check to make sure that it ** conforms to the rules for identifiers and that the publication ** actually exists. Disallow NULL. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END /* ** Get subscriptions */ if @publication <>'%' begin execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode<>0 Return (1) end create table #helpsubscription ( publication sysname collate database_default not null, publisher sysname collate database_default not null, publisher_db sysname collate database_default not null, subscriber sysname collate database_default not null, subscriber_db sysname collate database_default not null, status int NOT NULL, subscriber_type int NOT NULL, subscription_type int NOT NULL, priority float(8) NOT NULL, sync_type tinyint NOT NULL, description nvarchar(255) collate database_default null, merge_jobid binary(16) NULL, full_publication tinyint NULL, use_interactive_resolver int NULL ) /* ** Performance Optimization: Eliminate the 'LIKE' clause for publication name. ** Empirical evidence shows almost 50% speed improvement when ** opening the cursor if publication name is provided. */ IF (@publication <> '%') insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type, subs1.use_interactive_resolver FROM sysmergesubscriptions subs1, sysmergesubscriptions subs2, MSmerge_replinfo replinfo, master..sysservers servers1, master..sysservers servers2, sysmergepublications pubs where subs1.subid <> subs2.subid and subs1.status <> 2 and subs2.subid = subs1.partnerid and pubs.pubid = subs1.pubid and pubs.pubid = subs2.pubid and servers1.srvid = subs1.srvid and servers2.srvid = subs2.srvid and pubs.name = @publication and replinfo.repid = subs1.subid and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1) and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default)) and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default)) and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default)) and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2) ELSE insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type, subs1.use_interactive_resolver FROM sysmergesubscriptions subs1, sysmergesubscriptions subs2, MSmerge_replinfo replinfo, master..sysservers servers1, master..sysservers servers2, sysmergepublications pubs where subs1.subid <> subs2.subid and subs1.status <> 2 and subs2.subid = subs1.partnerid and pubs.pubid = subs1.pubid and pubs.pubid = subs2.pubid and servers1.srvid = subs1.srvid and servers2.srvid = subs2.srvid and replinfo.repid = subs1.subid and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1) and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default)) and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default)) and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default)) and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2) if exists (select * from #helpsubscription) select @found = 1 else select @found = 0 if @no_row = 1 goto DONE CREATE TABLE #merge_agent_properties ( job_id VARBINARY(16) NOT NULL, offload_enabled bit NULL, offload_server sysname collate database_default null ) EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distributiondb OUTPUT IF @retcode <> 0 GOTO DONE SELECT @distributor = RTRIM(@distributor) -- Get distribution agent properties IF LOWER(@@SERVERNAME) <> LOWER(@distributor) BEGIN SELECT @distproc = @distributor + '.' + @distributiondb + '.dbo.sp_MSenum_merge_agent_properties' END ELSE BEGIN SELECT @distproc = @distributiondb + '.dbo.sp_MSenum_merge_agent_properties' END SELECT @dbname = db_name() INSERT INTO #merge_agent_properties EXEC @retcode = @distproc @publisher = @@SERVERNAME, @publisher_db = @dbname, @publication = @publication IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' or LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both' begin select 'subscription_name' = subscriber + ':' + subscriber_db, hs.publication, hs.publisher, hs.publisher_db, hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type, hs.subscription_type, hs.priority, hs.sync_type, hs.description, hs.merge_jobid, hs.full_publication, ap.offload_enabled, ap.offload_server, hs.use_interactive_resolver from #helpsubscription hs left outer join #merge_agent_properties ap on hs.merge_jobid = ap.job_id order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db end else begin select 'subscription_name' = hs.publisher + ':' + hs.publisher_db + ':' + hs.publication, hs.publication, hs.publisher, hs.publisher_db, hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type, hs.subscription_type, hs.priority, hs.sync_type, hs.description, hs.merge_jobid, hs.full_publication, ap.offload_enabled, ap.offload_server, hs.use_interactive_resolver from #helpsubscription hs left outer join #merge_agent_properties ap on hs.merge_jobid = ap.job_id order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db end drop table #merge_agent_properties select @retcode = 0 DONE: if (@cursor_open = 1) begin close #cursor deallocate #cursor end drop table #helpsubscription return @retcode go exec dbo.sp_MS_marksystemobject sp_helpmergesubscription go grant execute on dbo.sp_helpmergesubscription to public go raiserror('Creating procedure sp_dropmergesubscription', 0,1) GO CREATE PROCEDURE sp_dropmergesubscription( @publication sysname = NULL, /* Publication name */ @subscriber sysname = NULL, /* Subscriber server */ @subscriber_db sysname = NULL, /* Subscription database */ @subscription_type nvarchar(15) = 'both', /* Subscription type - push, pull, both */ @ignore_distributor bit = 0, @reserved bit = 0 )AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @subscriber_bit smallint declare @subscriber_type smallint declare @subscriber_srvid int declare @publisher_srvid int declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @partnerid uniqueidentifier declare @subscription_type_id int declare @found_subscription int declare @local_server sysname declare @local_db sysname declare @cmd nvarchar(290) declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @pubidstr nvarchar(38) declare @publisher sysname declare @publisher_db sysname declare @implicit_transaction int declare @close_cursor_at_commit int select @close_cursor_at_commit = 0 select @implicit_transaction = 0 /* ** Save setting values first before changing them */ IF (@reserved = 0) BEGIN SELECT @implicit_transaction = @@options & 2 SELECT @close_cursor_at_commit = @@options & 4 SET IMPLICIT_TRANSACTIONS OFF SET CURSOR_CLOSE_ON_COMMIT OFF END /* ** Initializations. */ set @subscriber_bit = 4 set @subscription_type_id = -1 set @found_subscription = 0 set @local_db = DB_NAME() set @local_server = @@SERVERNAME set @publisher = @@SERVERNAME set @publisher_db = DB_NAME() /* ** Check to see if current database is enabled for publishing/subscribing */ IF not exists (select name from sysobjects where name='sysmergesubscriptions') BEGIN RAISERROR (14055, 16, -1) RETURN (1) END /* ** Parameter Check: @subscription_type. ** Set subscription_typeid based on the @subscription_type specified. ** ** subscription_type subscription_type ** ================= =============== ** 0 push ** 1 pull */ if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('both', 'push', 'pull') BEGIN RAISERROR (14128, 16, -1) RETURN (1) END IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both' begin EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = 'push', @ignore_distributor = @ignore_distributor, @reserved = 1 if @retcode<>0 or @@ERROR<>0 return (1) EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = 'pull', @ignore_distributor = @ignore_distributor, @reserved = 1 if @retcode<>0 or @@ERROR<>0 return (1) return (0) end IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' set @subscription_type_id = 0 else set @subscription_type_id = 1 /* ** Parameter validation (different for push and pull modes) */ IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' begin /* ** Assign parameter values appropriately */ if @publisher IS NULL set @publisher = @@SERVERNAME if (@publisher_db IS NULL) set @publisher_db = DB_NAME() /* ** Parameter Check: @subscriber ** Check to make sure that the subscriber is defined */ IF @subscriber IS NULL BEGIN RAISERROR (14043, 16, -1, '@subscriber') RETURN (1) END /* ** Parameter Check: @subscriber_db */ IF @subscriber_db IS NULL BEGIN select @subscriber_db = 'all' END end else begin /* ** Assign parameter values appropriately */ if @subscriber IS NULL set @subscriber = @@SERVERNAME if @subscriber_db IS NULL set @subscriber_db = DB_NAME() /* ** Parameter Check: @publisher ** Check to make sure that the publisher is defined */ IF @publisher IS NULL BEGIN RAISERROR (14043, 16, -1, '@publisher') RETURN (1) END EXECUTE @retcode = dbo.sp_validname @publisher IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) /* ** Parameter Check: @publisher_db */ IF @publisher_db IS NULL BEGIN RAISERROR (14043, 16, -1, '@publisher_db') RETURN (1) END end /* ** Parameter Check: @publication. ** If the publication name is specified, check to make sure that it ** conforms to the rules for identifiers and that the publication ** actually exists. Disallow NULL. */ if @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END IF LOWER(@publication) = 'all' BEGIN declare hC1 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergepublications where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() FOR READ ONLY OPEN hC1 FETCH hC1 INTO @publication WHILE (@@fetch_status <> -1) BEGIN EXECUTE dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = @subscription_type, @ignore_distributor = @ignore_distributor, @reserved = 1 FETCH hC1 INTO @publication END CLOSE hC1 DEALLOCATE hC1 RETURN (0) END if NOT EXISTS (select * FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()) BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END IF LOWER(@subscriber) = 'all' BEGIN declare hC2 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT srvname FROM master..sysservers WHERE (srvstatus & 4 <> 0) FOR READ ONLY OPEN hC2 FETCH hC2 INTO @subscriber WHILE (@@fetch_status <> -1) BEGIN EXECUTE dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = @subscription_type, @ignore_distributor = @ignore_distributor, @reserved = 1 FETCH hC2 INTO @subscriber END CLOSE hC2 DEALLOCATE hC2 RETURN (0) END /* ** Validate that the subscriber is a valid server */ select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default IF @subscriber_srvid IS NULL BEGIN --RAISERROR (14010, 16, -1) RETURN (1) END /* ** NOTE: remove this batch */ IF LOWER(@subscriber_db) = 'all' BEGIN declare hC3 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT db_name FROM sysmergesubscriptions WHERE srvid = @subscriber_srvid AND subid <> pubid AND sysmergesubscriptions.pubid = @pubid AND sysmergesubscriptions.subscription_type = @subscription_type_id FOR READ ONLY OPEN hC3 FETCH hC3 INTO @subscriber_db WHILE (@@fetch_status <> -1) BEGIN EXECUTE dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = @subscription_type, @ignore_distributor = @ignore_distributor, @reserved = 1 FETCH hC3 INTO @subscriber_db END CLOSE hC3 DEALLOCATE hC3 RETURN (0) END /* ** Validate that the publisher is a valid server */ select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default IF @publisher_srvid IS NULL BEGIN --RAISERROR (14010, 16, -1) RETURN (1) END select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL return (0) if exists (select * from sysmergesubscriptions where subid=@pubid and pubid=@pubid and db_name<>db_name()) RETURN (0) /* ** Get subscriptions from either local replicas or global replicas */ select @subid = subs1.subid, @partnerid = subs2.subid, @subscriber_type = subs1.subscriber_type from sysmergesubscriptions subs1, sysmergesubscriptions subs2, sysmergepublications pubs where subs1.srvid = @subscriber_srvid and subs1.db_name = @subscriber_db and subs2.srvid = @publisher_srvid and subs2.db_name = @publisher_db and subs1.pubid = subs2.subid and subs2.pubid = pubs.pubid and pubs.name = @publication and UPPER(pubs.publisher)=UPPER(@@servername) and pubs.publisher_db=db_name() and subs1.subscription_type = @subscription_type_id and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1) if @subid IS NULL begin -- raiserror (14050, 16, -1) RETURN (0) end begin tran save TRAN dropmergesubscription /* ** Do not drop the subscription corresponding to the loopback subscription */ if (@subid <> @partnerid) begin /* ** global/republisher subscriptions have to stay for a while even after being ** dropped so that they won't regain lives for themselves. They would be cleanup eventually. */ if (@subscriber_type<>1) begin delete from sysmergesubscriptions where subid = @subid IF @@ERROR <> 0 GOTO FAILURE delete MSmerge_replinfo WHERE repid = @subid IF @@ERROR <> 0 GOTO FAILURE end else begin update sysmergesubscriptions set status=2 where subid=@subid IF @@ERROR<>0 GOTO FAILURE end /* ** The MobileSync registry entry needs to be dropped only for push subscriptions - ** i.e - need not be called when a pull subscription is created at the ** subscriber and sp_addmergesubscription is being called then. */ IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' begin /* Call sp_MSunregistersubscription so that the reg entries get deleted */ exec @retcode = dbo.sp_MSunregistersubscription @publisher = @@SERVERNAME, @publisher_db = @publisher_db, @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db IF @retcode<>0 or @@ERROR<>0 GOTO FAILURE END end /* ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC. */ if @ignore_distributor = 0 begin /* ** Get distribution server information for remote RPC call. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 BEGIN GOTO FAILURE END SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSdrop_merge_subscription' EXEC @retcode = @distproc @@SERVERNAME, @publisher_db, @publication, @subscriber, @subscriber_db, @subscription_type IF @@ERROR <> 0 OR @retcode <> 0 begin goto FAILURE end end /* ** If last subscription is dropped and the DB is not enabled for publishing, ** then remove the merge system tables */ IF (not exists (select * from sysmergesubscriptions )) AND (select category & 4 FROM master..sysdatabases WHERE name = DB_NAME() collate database_default)=0 BEGIN execute @retcode = dbo.sp_MSdrop_mergesystables if @@ERROR <> 0 or @retcode <> 0 begin return (1) end END COMMIT TRAN /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END RETURN(0) FAILURE: /* UNDONE : This code is specific to 6.X nested transaction semantics */ RAISERROR (14056, 16, -1) if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION dropmergesubscription COMMIT TRANSACTION end /* ** Set back original settings */ IF @reserved = 0 BEGIN IF @implicit_transaction <>0 SET IMPLICIT_TRANSACTIONS ON IF @close_cursor_at_commit <>0 SET CURSOR_CLOSE_ON_COMMIT ON END return (1) go exec dbo.sp_MS_marksystemobject sp_dropmergesubscription go grant execute on dbo.sp_dropmergesubscription to public go raiserror('Creating procedure sp_MShelpvalidationdate', 0,1) GO CREATE PROCEDURE sp_MShelpvalidationdate( @publication sysname, @subscriber sysname, @subscriber_db sysname )AS declare @pubid uniqueidentifier declare @subscriber_srvid int select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL return (0) select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default select last_validated, attempted_validate from sysmergesubscriptions where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid return (0) GO exec dbo.sp_MS_marksystemobject sp_MShelpvalidationdate go raiserror('Creating procedure sp_MSmergepublishdb', 0,1) GO CREATE PROCEDURE sp_MSmergepublishdb( @value sysname, @ignore_distributor bit = 0 ) AS SET NOCOUNT ON /* ** Declarations. */ declare @command nvarchar(255) declare @description nvarchar(500) declare @cmptlevel tinyint declare @db_name sysname declare @retcode int declare @distributor sysname declare @distribdb sysname declare @category_name sysname declare @agentname sysname declare @working_directory nvarchar(255) /* ** Initialization */ select @db_name = DB_NAME() /* ** Parameter check ** @value */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false') BEGIN RAISERROR(14137,16,-1) RETURN(1) END /* ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC. */ if @ignore_distributor = 0 begin /* ** Test to see if the distributor is installed and online. */ EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @directory=@working_directory OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 or @distributor IS NULL or @distribdb IS NULL BEGIN IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' RAISERROR (20028, 16, -1) ELSE RAISERROR (20029, 16, -1) RETURN (1) END end /* ** Enable the database for publishing. */ IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' BEGIN select @cmptlevel = cmptlevel from master..sysdatabases where name=db_name() collate database_default if @cmptlevel<70 OR @cmptlevel is NULL begin RAISERROR(20061, 16, -1) goto FAILURE end execute @retcode = dbo.sp_MScreate_mergesystables if @@ERROR <> 0 or @retcode <> 0 begin goto FAILURE end END ELSE /* Disable the database for publishing. */ BEGIN /* ** Remove all the registration entries for subscriptions */ if not exists(select * from sysobjects where name = 'sysmergesubscriptions') goto FAILURE exec @retcode = dbo.sp_dropmergesubscription @publication = 'all', @subscriber = 'all', @subscriber_db = 'all', @subscription_type = 'both', @ignore_distributor = @ignore_distributor IF @@ERROR <> 0 or @retcode <> 0 begin goto FAILURE end /* ** Remove all publications and articles in the database. */ EXEC @retcode = dbo.sp_dropmergepublication @publication = 'all', @ignore_distributor = @ignore_distributor IF @@ERROR <> 0 or @retcode <> 0 begin -- sp_dropmergepublication will raiserror goto FAILURE end If NOT EXISTS (select * from sysmergepublications) BEGIN execute @retcode = dbo.sp_MSdrop_mergesystables if @@ERROR <> 0 or @retcode <> 0 begin goto FAILURE end END END return 0 FAILURE: return (1) GO exec dbo.sp_MS_marksystemobject sp_MSmergepublishdb go raiserror('Creating procedure sp_enumcustomresolvers', 0,1) GO CREATE PROCEDURE sp_enumcustomresolvers -- @distributor parameter will be removed in the next version. @distributor sysname = NULL AS SET NOCOUNT ON declare @distributor_rpc sysname declare @return_status int declare @distproc nvarchar(150) declare @retcode int select @return_status = 0 /* ** Get the distributor ** Use local RPC if @distributor == @servername. This is used by UI ** before installing a distributor. */ if @distributor = @@servername select @distributor_rpc = @@servername else begin EXEC @return_status = dbo.sp_helpdistributor @rpcsrvname = @distributor_rpc OUTPUT IF @@error <> 0 OR @return_status <> 0 OR @distributor_rpc IS NULL BEGIN RAISERROR (20036, 16, -1) RETURN (1) END end declare @key_exists int select @key_exists = 0 create table #keyexists (keyexists int) select @distproc = RTRIM(@distributor_rpc) + '.master..xp_regread' insert into #keyexists exec @distproc 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver' select @key_exists = keyexists from #keyexists if (@key_exists = 1) begin select @distproc = RTRIM(@distributor_rpc) + '.master..xp_regenumvalues' exec @distproc 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver' if @@ERROR<>0 return (1) end drop table #keyexists RETURN (0) GO exec dbo.sp_MS_marksystemobject sp_enumcustomresolvers go grant execute on dbo.sp_enumcustomresolvers to public go raiserror('Creating procedure sp_changemergefilter', 0,1) GO create procedure sp_changemergefilter( @publication sysname, @article sysname, @filtername sysname, @property sysname, @value nvarchar(2000), @force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ )AS set nocount on declare @pre_command int declare @qual_object nvarchar(132) declare @qual_join_object nvarchar(132) declare @join_articlename nvarchar(4000) declare @db_name sysname declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @object sysname declare @join_object sysname declare @retcode int declare @join_filterid int declare @join_objid int declare @join_nickname int declare @snapshot_ready int /* ** Security Check. ** Only the System Administrator (SA) or the Database Owner (dbo) can ** call this procedure */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) /* ** Parameter Check: @join_articlename. ** The join_articlename cannot be NULL */ if @filtername is NULL begin raiserror (14043, 11, -1, '@filtername') return (1) end if @value is NULL or @value = '' begin raiserror (14043, 11, -1, '@value') return (1) end /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14043, 11, -1, '@publication') return (1) end /* ** Get the pubid and make sure the publication exists */ select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end select @db_name = db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> db_name() BEGIN RAISERROR (20047, 16, -1) RETURN (1) END /* ** Parameter Check: @article. ** Check to see that the @article is valid and does exist */ if @article is NULL begin raiserror (20045, 16, -1) return (1) end select @artid = artid, @object=object_name(objid), @pre_command=pre_creation_command from sysmergearticles where name = @article and pubid = @pubid if @artid is NULL begin raiserror (20046, 16, -1) return (1) end select @join_filterid=join_filterid, @join_articlename=join_articlename from sysmergesubsetfilters where pubid=@pubid and artid=@artid and filtername=@filtername if @join_filterid is null begin raiserror (21362, 16, -1, @filtername) return (1) end IF @property IS NULL BEGIN CREATE TABLE #temp (properties sysname collate database_default) INSERT INTO #temp VALUES ('filtername') INSERT INTO #temp VALUES ('join_filterclause') INSERT INTO #temp VALUES ('join_articlename') INSERT INTO #temp VALUES ('join_unique_key') select * FROM #tab1 RETURN (0) END if @value is null begin RAISERROR (14043, 16, -1, @property) return (1) end begin TRAN save TRAN change_filter if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('join_filterclause','join_articlename','join_unique_key') and @snapshot_ready>0 begin -- 1 means'drop': which is the only option that support reintialization if @pre_command<>1 begin raiserror(21419, 16, -1, @filtername, @article) goto FAILURE end /* ** make sure we know we really want to do this. */ if @force_invalidate_snapshot = 0 begin raiserror(20607, 16, -1) goto FAILURE end if @force_reinit_subscription = 0 begin raiserror(20608, 16, -1) goto FAILURE end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 goto FAILURE exec @retcode = sp_MSreinitmergepublication @publication if @retcode<>0 or @@ERROR<>0 goto FAILURE end IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_filterclause' BEGIN /* check the validity of join_filterclause */ select @join_object=object_name(objid) from sysmergearticles where name=@join_articlename and pubid=@pubid select @qual_object=QUOTENAME(@object) select @qual_join_object=QUOTENAME(@join_object) exec ('declare @test int select @test=1 from ' + @qual_object + ', ' + @qual_join_object + ' where ' + @value) if @@ERROR<>0 begin raiserror(21256, 16, -1, @value, @object) goto FAILURE end update sysmergesubsetfilters set join_filterclause=@value where join_filterid=@join_filterid execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode<>0 goto FAILURE END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_unique_key' BEGIN IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false') BEGIN RAISERROR(14137,16,-1) RETURN(1) END if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' update sysmergesubsetfilters set join_unique_key=1 where join_filterid=@join_filterid else update sysmergesubsetfilters set join_unique_key=0 where join_filterid=@join_filterid if @@ERROR <> 0 or @retcode<>0 goto FAILURE END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='filtername' BEGIN update sysmergesubsetfilters set filtername=@value where join_filterid=@join_filterid if @@ERROR<>0 goto FAILURE END IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_articlename' BEGIN select @join_objid = objid from sysmergearticles where name = @value and pubid = @pubid IF @join_objid is NULL BEGIN raiserror (14027, 11, -1, @value) goto FAILURE END select @join_nickname = nickname from sysmergearticles where pubid = @pubid AND objid = @join_objid if @join_nickname is NULL begin raiserror (20001, 11, -1, @article, @publication) goto FAILURE end update sysmergesubsetfilters set join_articlename=@value, join_nickname=@join_nickname where join_filterid=@join_filterid if @@ERROR<>0 goto FAILURE END Commit TRAN return(0) FAILURE: if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION change_filter COMMIT TRANSACTION end RAISERROR (20038, 16, -1, @article, @publication) return(1) go exec dbo.sp_MS_marksystemobject sp_changemergefilter go grant execute on dbo.sp_changemergefilter to public go raiserror('Creating procedure sp_addmergefilter', 0,1) GO create procedure sp_addmergefilter( @publication sysname, /* publication name */ @article sysname, /* article name */ @filtername sysname, /* join filter name */ @join_articlename sysname, /* Name of the table being joined to the base table */ @join_filterclause nvarchar(2000), /* filter clause qualifying the join */ @join_unique_key int = 0, @force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */ @force_reinit_subscription bit = 0 /* Force reinit subscription */ )AS set nocount on /* ** Declarations. */ declare @snapshot_ready int declare @db_name sysname declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @art_nickname int declare @join_nickname int declare @db sysname declare @qual_object nvarchar(150) declare @qual_join_object nvarchar(150) declare @qual_object_view nvarchar(150) declare @qual_join_object_view nvarchar(150) declare @object sysname declare @vertical int declare @join_vertical int declare @join_object nvarchar(140) declare @object_view nvarchar(140) declare @join_object_view nvarchar(140) declare @owner sysname declare @retcode int declare @join_objid int declare @objid int declare @status int declare @column_list nvarchar(4000) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Security Check. ** Only the System Administrator (SA) or the Database Owner (dbo) can ** add an article to a publication. */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) /* ** Parameter Check: @filtername. ** The join_filter_name cannot be NULL */ if @filtername is NULL begin raiserror (14043, 11, -1, @filtername) return (1) end if @join_filterclause is NULL or @join_filterclause = '' begin raiserror (14043, 11, -1, '@join_filterclause') return (1) end /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14003, 16, -1) return (1) end /* ** Get the pubid and make sure the publication exists */ select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end if @snapshot_ready>0 begin /* ** make sure we know we really want to do this. */ if @force_invalidate_snapshot = 0 begin raiserror(21366, 16, -1, @filtername) goto FAILURE end if @force_reinit_subscription = 0 begin raiserror(21367, 16, -1, @filtername) goto FAILURE end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 goto FAILURE exec @retcode = sp_MSreinitmergepublication @publication if @retcode<>0 or @@ERROR<>0 goto FAILURE end select @db_name = db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> db_name() BEGIN RAISERROR (20047, 16, -1) RETURN (1) END /* ** Parameter Check: @article. ** Check to see that the @article is valid and does exist */ if @article is NULL begin raiserror (20045, 16, -1) return (1) end select @artid = artid, @objid=objid, @vertical=vertical_partition, @art_nickname = nickname from sysmergearticles where name = @article and pubid = @pubid if @artid is NULL begin raiserror (20046, 16, -1) return (1) end /* ** Get the id of the @join_articlename */ select @join_objid = objid, @join_vertical= vertical_partition from sysmergearticles where name=@join_articlename and pubid = @pubid IF @join_objid is NULL BEGIN raiserror (14027, 11, -1, @join_articlename) return (1) END /* check the validity of join_filterclause */ select @object = object_name(@objid) select @join_object = object_name(@join_objid) select @object_view = @object select @join_object_view = @join_object /* is current article vertically partitioned */ if @vertical=1 begin select @object_view = 'OBJECT_VIEW_' + @object select @qual_object_view=QUOTENAME(@object_view) select @qual_object=QUOTENAME(@object) exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @qual_object) if @@ERROR<>0 goto FAILURE end /* is join article vertically partitioned */ if @join_vertical=1 begin select @join_object_view = 'JOIN_OBJECT_VIEW_' + @join_object select @qual_join_object_view = QUOTENAME(@join_object_view) select @qual_join_object=QUOTENAME(@join_object) exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @join_objid exec ('create view ' + @qual_join_object_view + ' as select ' + @column_list + ' from ' + @qual_join_object) if @@ERROR<>0 goto FAILURE end select @qual_object_view=QUOTENAME(@object_view) select @qual_join_object_view = QUOTENAME(@join_object_view) select @qual_join_object=QUOTENAME(@join_object) select @qual_object=QUOTENAME(@object) exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @qual_object + ', ' + @qual_join_object_view + ' ' + @qual_join_object + ' where ' + @join_filterclause) if @@ERROR<>0 begin if @vertical=1 exec ('drop view ' + @qual_object_view) if @join_vertical=1 exec ('drop view ' + @qual_join_object_view) raiserror(21256, 16, -1, @join_filterclause, @article) return (1) end if @vertical=1 exec ('drop view ' + @qual_object_view) if @join_vertical=1 exec ('drop view ' + @qual_join_object_view) select @join_nickname = nickname from sysmergearticles where pubid = @pubid AND objid = @join_objid if @join_nickname is NULL begin raiserror (20001, 11, -1, @article, @publication) return (1) end IF NOT EXISTS (select * from sysmergearticles where pubid=@pubid AND nickname = @join_nickname) BEGIN RAISERROR (20046, 16, -1) /* Only the original publisher can do so */ RETURN (1) END /* ** Make sure that the table name specified is a table and not a view. */ if NOT exists (select * from sysobjects where id = @join_objid AND type = 'U') begin raiserror (14028, 16, -1) return (1) end /* ** Add the join filter to sysmergesubsetfilters if it is not already there */ IF exists (select * from sysmergesubsetfilters where filtername=@filtername and pubid=@pubid and artid=@artid) begin raiserror (20002, 16, -1, @filtername, @article, @publication) return (1) end insert INTO sysmergesubsetfilters(filtername, pubid, artid, art_nickname, join_articlename, join_nickname, join_unique_key, join_filterclause) values(@filtername, @pubid, @artid, @art_nickname, @join_articlename, @join_nickname, @join_unique_key, @join_filterclause) if @@error <> 0 begin goto FAILURE end execute @retcode = dbo.sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode <>0 goto FAILURE return (0) FAILURE: RAISERROR (20038, 16, -1, @article, @publication) return (1) go exec dbo.sp_MS_marksystemobject sp_addmergefilter go grant execute on dbo.sp_addmergefilter to public go raiserror('Creating procedure sp_dropmergefilter', 0,1) GO create procedure sp_dropmergefilter @publication sysname, /* publication name */ @article sysname, /* article name */ @filtername sysname, /* Name of the table being joined to the base table */ @force_invalidate_snapshot bit = 0 AS set nocount on /* ** Declarations. */ declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @join_objid int declare @retcode int declare @join_filterid int declare @db_name sysname declare @allow_anonymous int declare @snapshot_ready tinyint /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* ** Security Check. */ exec @retcode=sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14003, 16, -1) return (1) end /* ** Get the pubid, and check if this publication exists. */ select @pubid = pubid, @snapshot_ready=snapshot_ready, @allow_anonymous=allow_anonymous from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication ) return (1) end select @db_name = db_name from sysmergesubscriptions where (pubid=@pubid) and (subid=@pubid) IF @db_name <> db_name() BEGIN RAISERROR (20047, 16, -1) RETURN (1) END /* ** Parameter Check: @article. ** Check to see that the @article is valid, and if it exists */ if @article is NULL begin raiserror (20045, 16, -1) return (1) end if @snapshot_ready>0 and (@allow_anonymous=1 or exists (select * from sysmergesubscriptions where pubid=@pubid and subid<>pubid and status=1)) BEGIN RAISERROR (21372, 16, -1, @filtername, @publication) RETURN (1) END if @snapshot_ready>0 begin if @force_invalidate_snapshot = 0 begin raiserror(21382, 16, -1, @filtername) return (1) end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 return (1) end select @artid = artid from sysmergearticles where name = @article and pubid = @pubid if @artid is NULL begin raiserror (20046, 16, -1) return (1) end select @join_filterid = join_filterid from sysmergesubsetfilters where pubid = @pubid AND artid= @artid AND filtername=@filtername /* ** Remove the join filter from sysmergesubsetfilters */ delete from sysmergesubsetfilters where join_filterid = @join_filterid if @@error <> 0 begin goto FAILURE end /* ** set the pub type to subset or full as appropriate */ exec @retcode=sp_MSsubsetpublication @publication if @@ERROR <> 0 or @retcode<>0 begin goto FAILURE end return(0) FAILURE: RAISERROR (20039, 16, -1, @article, @publication) return (1) go exec dbo.sp_MS_marksystemobject sp_dropmergefilter go raiserror('Creating procedure sp_helpmergefilter', 0,1) GO create procedure sp_helpmergefilter @publication sysname, /* publication name */ @article sysname = '%', /* article name */ @filtername sysname = '%' AS set nocount on /* ** Declarations. */ declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @retcode int /* ** No security checking is needed for sp_help?? */ /* ** Calling sp_help* is all right whether current database is enabled for pub/sub or not */ IF not exists (select * from sysobjects where name='sysmergesubscriptions') RETURN (0) /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14003, 16, -1) return (1) end /* ** Get the pubid and check if the publication does exist */ select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end /* ** Parameter Check: @article. ** If an @article is specified, make sure it exists */ select @artid = artid from sysmergeextendedarticlesview where name = @article and pubid = @pubid if @artid is NULL and (@article <> '%' or @filtername <> '%') begin raiserror (20046, 16, -1) return (1) end /* ** Return the join filters from sysmergesubsetfilters */ IF @filtername <> '%' select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key, 'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name, 'article name' = b_a.name from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u, sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a where f.pubid = @pubid AND b_a.pubid = @pubid AND f.filtername = @filtername AND f.artid = @artid AND f.artid = b_a.artid AND b_o.id = b_a.objid AND b_u.uid = b_o.uid AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid AND j_u.uid = j_o.uid ORDER BY j_o.name, b_o.name ELSE begin if @artid is not null select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key, 'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name, 'article name' = b_a.name from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u, sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a where f.pubid = @pubid AND b_a.pubid = @pubid AND f.artid = @artid AND f.artid = b_a.artid AND b_o.id = b_a.objid AND b_u.uid = b_o.uid AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid AND j_u.uid = j_o.uid ORDER BY j_o.name, b_o.name else select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key, 'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name, 'article name' = b_a.name from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u, sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a where f.pubid = @pubid AND b_a.pubid = @pubid AND f.artid = b_a.artid AND b_o.id = b_a.objid AND b_u.uid = b_o.uid AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid AND j_u.uid = j_o.uid ORDER BY j_o.name, b_o.name end return(0) go exec dbo.sp_MS_marksystemobject sp_helpmergefilter go grant execute on dbo.sp_helpmergefilter to public go raiserror('Creating procedure sp_MSscript_dri', 0,1) go create procedure sp_MSscript_dri (@publication sysname, @article sysname) AS declare @pubid uniqueidentifier select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() select rkeyid, fkeyid from sysreferences where fkeyid in (select objid from sysmergearticles where pubid = @pubid and name = @article) and rkeyid not in (select objid from sysmergearticles where pubid = @pubid) go exec dbo.sp_MS_marksystemobject sp_MSscript_dri go grant execute on dbo.sp_MSscript_dri to public go raiserror('Creating procedure sp_MSenumpubreferences', 0,1) GO create procedure sp_MSenumpubreferences (@publication sysname) as declare @pubid uniqueidentifier declare @retcode int /* ** Security Check. */ exec @retcode = dbo.sp_MSreplcheck_publish if @@ERROR <> 0 or @retcode <> 0 return(1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select distinct ReferencingObject = object_name(rkeyid), ArticleObject = object_name(fkeyid) from sysreferences r, sysmergearticles where r.fkeyid in (select objid from sysmergearticles where pubid = @pubid) and r.rkeyid not in (select objid from sysmergearticles where pubid = @pubid) select distinct ReferencedObject = object_name(fkeyid), ArticleObject = object_name(rkeyid) from sysreferences r where r.rkeyid in (select objid from sysmergearticles where pubid = @pubid) and r.fkeyid not in (select objid from sysmergearticles where pubid = @pubid) return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumpubreferences go grant execute on dbo.sp_MSenumpubreferences to public go raiserror('Creating procedure sp_MSsubsetpublication', 0,1) GO create procedure sp_MSsubsetpublication (@publication sysname) as declare @pubid uniqueidentifier declare @false bit declare @true bit declare @boolean_filter bit declare @join_filter bit declare @full int declare @subset int declare @unsynced int /* ** Initializations */ select @true = 1 select @false = 0 select @full = 0 /* Const: publication type 'full' */ select @subset = 1 /* Const: publication type 'subset' */ select @unsynced = 1 if not exists (select * from sysobjects where name = 'sysmergepublications') BEGIN RAISERROR (20054, 16, -1) RETURN (1) END select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END /* ** Set the publication_type to subset if the publication has either a boolean or a join filter */ if exists (select * from sysmergearticles where pubid = @pubid and len(subset_filterclause) > 0) set @boolean_filter = @true if exists (select * from sysmergesubsetfilters where pubid = @pubid) set @join_filter = @true /* ** For subset publications set the article status to be unsynced so that the triggers can be regenerated. */ if (@boolean_filter = 1 OR @join_filter = 1) begin update sysmergepublications set publication_type = @subset where pubid = @pubid if @@ERROR <> 0 return (1) update sysmergearticles set status = @unsynced where pubid = @pubid if @@ERROR <> 0 return (1) end else update sysmergepublications set publication_type = @full where pubid = @pubid if @@ERROR <> 0 return (1) return(0) go exec dbo.sp_MS_marksystemobject sp_MSsubsetpublication go raiserror('Creating procedure sp_MSindexcolfrombin', 0,1) GO create procedure sp_MSindexcolfrombin @object_id int, @col_index int, @colids_bin varbinary(256), @colname sysname output, @columns varbinary(128) = NULL AS /* Declare variables */ declare @start_byte int declare @colid int declare @retcode int set @colid = unicode( substring( convert( nvarchar(128),@colids_bin ), @col_index, 1 ) ) select @colname = QUOTENAME(name) from syscolumns where id = @object_id and colid = @colid /* vertical partitioning is ON */ if @columns is not NULL begin /* see if this column is currently in the vertical partitioning */ exec @retcode = sp_MStestbit @bm=@columns, @coltotest=@colid if @retcode=0 select @colname = NULL end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSindexcolfrombin go raiserror('Creating procedure sp_MSmakejoinfilter', 0,1) GO create procedure sp_MSmakejoinfilter @publication sysname, @article sysname, @base_objid int, @join_objid int, @join_unique int AS /* Declare additional variables */ declare @table_name nvarchar(140) declare @join_table nvarchar(140) declare @join_article sysname declare @filt_name sysname declare @basecol sysname declare @joincol sysname declare @keycnt int declare @basekeys varbinary(32) declare @joinkeys varbinary(32) declare @base_columns varbinary(128) declare @join_columns varbinary(128) declare @keyindex int declare @filtclause nvarchar(3000) declare @filtpiece nvarchar(500) declare @qual_jointable nvarchar(270) declare @retcode smallint declare @pubid uniqueidentifier declare @first_piece bit select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() select @base_columns=0x00 -- so that no base column name will be returned if following query does not find a match select @join_columns=0x00 -- same as above select @base_columns=columns from sysmergearticles where pubid=@pubid and objid=@base_objid select @join_columns=columns from sysmergearticles where pubid=@pubid and objid=@join_objid /* Are we adding join filter on referencing table (@join_unique = 1) or on unique key table ? */ if @join_unique = 1 /* Select the keycnt, fkeys, rkeys, and filter name from sysreferences */ select @keycnt = keycnt, @basekeys = forkeys, @joinkeys = refkeys, @filt_name = object_name(constid) from sysreferences where fkeyid = @base_objid and rkeyid = @join_objid else select @keycnt = keycnt, @basekeys = refkeys, @joinkeys = forkeys, @filt_name = object_name(constid) from sysreferences where rkeyid = @base_objid and fkeyid = @join_objid /* Set up object names - we use them as correlation values */ set @table_name = QUOTENAME(object_name(@base_objid)) set @join_table = QUOTENAME(object_name(@join_objid)) select @join_article = name from sysmergearticles where objid = @join_objid and pubid=@pubid -- set @qual_jointable = @join_owner + '.' + @join_table /* Loop over keys, building up our join filter clause */ set @keyindex = 1 set @first_piece = 0 while @keyindex <= @keycnt begin /* Get the column names */ /* also pass in vertical partitioning binary to excluded columns that are not in the current partition */ exec dbo.sp_MSindexcolfrombin @base_objid, @keyindex, @basekeys, @basecol output, @base_columns if @@ERROR<>0 return (1) exec dbo.sp_MSindexcolfrombin @join_objid, @keyindex, @joinkeys, @joincol output, @join_columns if @@ERROR<>0 return (1) if @basecol is not NULL and @joincol is not NULL begin /* Make the piece of predicate pertaining to this key column */ set @filtpiece = @table_name + '.' + @basecol + ' = ' + @join_table + '.' + @joincol /* If first time through, initialize clause, else add to it */ if @first_piece=0 begin set @first_piece=1 set @filtclause = @filtpiece end else set @filtclause = @filtclause + ' and ' + @filtpiece end /* move on to the next key */ set @keyindex = @keyindex + 1 end /* no filter generated due to vertical partitioning */ if @first_piece>0 begin /* Add the join filter */ exec @retcode = dbo.sp_addmergefilter @publication, @article, @filt_name, @join_article, @filtclause, @join_unique if @@ERROR<>0 or @retcode<>0 return (1) end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSmakejoinfilter go raiserror('Creating procedure sp_MSmakeexpandproc', 0,1) GO create procedure sp_MSmakeexpandproc @pubname sysname, @filterid int, @procname sysname AS /* Declare additional variables */ declare @pubid uniqueidentifier declare @base_nick int declare @join_nick int declare @base_nickstr nvarchar(10) declare @join_nickstr nvarchar(10) declare @filterid_str nvarchar(10) declare @base_objid int declare @join_objid int declare @base_table nvarchar(270) declare @before_viewname nvarchar(270) declare @join_table nvarchar(270) declare @base_owner nvarchar(270) declare @join_owner nvarchar(270) declare @join_clause nvarchar(4000) declare @retcode int declare @must_check int declare @view_type int declare @guidcolname nvarchar(270) declare @joinguidname nvarchar(270) declare @view_objid int declare @view_name nvarchar(270) declare @cmd_piece nvarchar(4000) select @pubid = pubid from sysmergepublications where name = @pubname and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() -- this procedure is to be called by xp_execresultset, so -- we create a temp table, put command pieces into it, and select them out -- create temp table to select the command text out of create table #tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) /* Figure out base table, join table for this join filter */ select @base_nick = art_nickname, @join_nick = join_nickname, @join_clause = join_filterclause from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid select @base_objid = objid, @view_type = view_type, @view_objid = sync_objid from sysmergearticles where pubid = @pubid and nickname = @base_nick select @join_objid = objid, @before_viewname = object_name(before_view_objid) from sysmergearticles where pubid = @pubid and nickname = @join_nick select @base_table = QUOTENAME(name), @base_owner = QUOTENAME(user_name(uid)) from sysobjects where id = @base_objid select @join_table = QUOTENAME(name), @join_owner = QUOTENAME(user_name(uid)) from sysobjects where id = @join_objid select @guidcolname = name from syscolumns where id = @base_objid and columnproperty (id, name, 'isrowguidcol')=1 select @joinguidname = name from syscolumns where id = @join_objid and columnproperty (id, name, 'isrowguidcol')=1 select @view_name = object_name(@view_objid) -- Quote the viewname. It is made from pub name which may have odd characters. set @view_name = QUOTENAME(@view_name) set @base_nickstr = convert(nchar(10), @base_nick) set @join_nickstr = convert(nchar(10), @join_nick) set @filterid_str = convert(nchar(10), @filterid) set @cmd_piece = 'create procedure ' + @procname + ' @belong int AS ' insert into #tempcmd(cmdtext) values (@cmd_piece) set @cmd_piece = 'if @belong = 1 begin /* Do a bulk insert to expand #belong */ update #belong set flag = ' + @filterid_str + ' where flag < ' + @filterid_str + ' insert into #belong (tablenick, rowguid, flag, skipexpand) select distinct ' + @base_nickstr + ', ' + @base_table + '.rowguidcol, 0, 0 from ' + @base_owner + '.' + @base_table + ', ' + @join_owner + '.' + @join_table + ', #belong b where (' + @join_clause + ') and ' + @join_table + '.rowguidcol = b.rowguid and b.tablenick = ' + @join_nickstr + ' and skipexpand = 0 ' insert into #tempcmd(cmdtext) values (@cmd_piece) set @cmd_piece = ' if @@ERROR <>0 return (1) /* Delete duplicates */ delete from #belong where skipexpand = 1 and rowguid in (select rowguid from #belong where flag = 0) delete from #belong where flag = 0 and rowguid in (select rowguid from #belong where flag <> 0) end ' insert into #tempcmd(cmdtext) values (@cmd_piece) /* Will we have to check rows that we add to #notbelong? */ if exists (select * from sysmergearticles where pubid = @pubid and nickname = @join_nick and len(subset_filterclause) > 0) set @must_check = 1 else if exists (select * from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid and join_unique_key <> 1) set @must_check = 1 else if not exists (select * from sysmergesubsetfilters where pubid = @pubid and art_nickname = @base_nick and join_filterid <> @filterid) set @must_check = 0 set @cmd_piece = ' else begin update #notbelong set flag = ' + @filterid_str + ' where flag < ' + @filterid_str + ' /* Do a bulk insert to expand #notbelong */ insert into #notbelong (tablenick, rowguid, flag) select distinct ' + @base_nickstr + ', ' + @base_table + '.rowguidcol, -1 from ' + @base_owner + '.' + @base_table + ', ' + @join_owner + '.' + @join_table + ', #notbelong nb where (' + @join_clause + ') and ' + @join_table + '.rowguidcol = nb.rowguid and nb.tablenick = ' + @join_nickstr + ' /* Remove duplicates */ delete from #notbelong where flag = -1 and rowguid in (select rowguid from #notbelong where flag <> -1) ' if @before_viewname is not null set @cmd_piece = @cmd_piece + ' insert into #notbelong (tablenick, rowguid, flag) select distinct ' + @base_nickstr + ', ' + @base_table + '.rowguidcol, -1 from ' + @base_owner + '.' + @base_table + ', ' + @before_viewname + ' ' + @join_table + ', #notbelong nb where (' + @join_clause + ') and ' + @join_table + '.' + @joinguidname + ' = nb.rowguid and nb.tablenick = ' + @join_nickstr + ' /* Remove duplicates */ delete from #notbelong where flag = -1 and rowguid in (select rowguid from #notbelong where flag <> -1) ' if @must_check = 0 begin insert into #tempcmd(cmdtext) values (@cmd_piece) set @cmd_piece = ' update #notbelong set flag = 0 where flag = -1 end ' insert into #tempcmd(cmdtext) values (@cmd_piece) end else if @view_type = 1 begin insert into #tempcmd(cmdtext) values (@cmd_piece) set @cmd_piece = ' -- We can do our check with a bulk delete, bulk update delete from #notbelong where flag = -1 and rowguid in (select ' + @guidcolname + ' from ' + @view_name + ') update #notbelong set flag = 0 where flag = -1 end ' insert into #tempcmd(cmdtext) values (@cmd_piece) end -- else we don't bother expanding #notbelong for this filter since there are cyclic -- join filters and this is not a unique key join. The cursored calls to sp_belongs -- are unacceptably slow, and there would still be cases where orphaned rows could occur. -- Now we select out the command text pieces in proper order so that our caller, -- xp_execresultset will execute the command that creates the stored procedure. select cmdtext from #tempcmd order by step GO exec dbo.sp_MS_marksystemobject sp_MSmakeexpandproc go raiserror('Creating procedure sp_MSdrop_expired_mergesubscription', 0,1) GO create procedure sp_MSdrop_expired_mergesubscription AS /* ** This stored procedure is to periodically check the status of all the subscriptions ** of every merge publication. If any of them is out-of-date, i.e., has lost contact ** with publisher for a certain length of time, we can declare the death of that replica ** and cleanup their traces at the publisher side */ declare @subscription_type int declare @sub_type nvarchar(5) declare @publication sysname declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @status tinyint declare @publisher_id int declare @subscriber sysname declare @subscriber_id int declare @subscriber_db sysname declare @publisher_db sysname declare @retention int -- in days declare @retcode smallint declare @recgen int declare @sentgen int declare @max_distretention int declare @recent_merge datetime declare @minus_retention2 datetime declare @minus_retention datetime declare @send_ts datetime declare @receive_ts datetime /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* we do not check return code here because we can tolerate any failure here */ exec @retcode = dbo.sp_MScleanup_conflict_table if @@ERROR<>0 or @retcode<>0 return (1) declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT p.name, p.pubid, p.retention from sysmergepublications p, sysmergesubscriptions s where s.subid=p.pubid and s.pubid=p.pubid and p.snapshot_ready=1 for read only open PC fetch PC into @publication, @pubid, @retention WHILE (@@fetch_status <> -1) BEGIN /* Compute the retention period cutoff dates per publication */ select @minus_retention2 = dateadd(day, -@retention * 2, getdate()) select @minus_retention = dateadd(day, -@retention, getdate()) if @retention is not NULL and @retention > 0 begin -- this is to reomve any user script (file and directory) when following two points fulfills -- 1. a snapshot has been run to pick it up -- 2. the snapshot is too old that no new subscription need this file exec @retcode = sp_MSremove_userscript @pubid if @@ERROR<>0 or @retcode<>0 begin close PC deallocate PC return (1) end declare SC CURSOR LOCAL FAST_FORWARD for select srvid, db_name, subid, status, subscription_type from sysmergesubscriptions where pubid = @pubid and pubid<>subid for read only open SC fetch SC into @subscriber_id, @subscriber_db, @subid, @status, @subscription_type WHILE (@@fetch_status <> -1) BEGIN select @subscriber=srvname from master..sysservers where srvid=@subscriber_id if @subscription_type = 0 select @sub_type = 'push' else select @sub_type = 'pull' select @receive_ts = coldate from MSmerge_genhistory where guidsrc = (select recguid from MSmerge_replinfo where repid = @subid) select @sentgen=sentgen from MSmerge_replinfo where repid=@subid select @send_ts = coldate from MSmerge_genhistory where generation=@sentgen if @receive_ts>@send_ts select @recent_merge = @receive_ts else select @recent_merge = @send_ts if @status <> 2 and @recent_merge<@minus_retention begin exec @retcode = dbo.sp_dropmergesubscription @publication = @publication, @subscriber = @subscriber, @subscriber_db = @subscriber_db, @subscription_type = @sub_type if @retcode <>0 or @@ERROR<>0 goto FAILURE raiserror(14157, 10, -1, @subscriber, @publication) end if @recent_merge<@minus_retention2 begin delete from sysmergesubscriptions where subid = @subid --delete the row in sysmergesubscription if @@ERROR<>0 goto FAILURE delete from MSmerge_replinfo where repid = @subid if @@ERROR<>0 goto FAILURE end fetch SC into @subscriber_id, @subscriber_db, @subid, @status, @subscription_type END CLOSE SC DEALLOCATE SC end fetch PC into @publication, @pubid, @retention END CLOSE PC DEALLOCATE PC return (0) FAILURE: close SC deallocate SC close PC deallocate PC return (1) GO exec dbo.sp_MS_marksystemobject sp_MSdrop_expired_mergesubscription go raiserror('Creating procedure sp_MScleanup_metadata', 0,1) GO create procedure sp_MScleanup_metadata @pubid uniqueidentifier AS declare @retcode int declare @truncate_flag int declare @pubid2 uniqueidentifier declare @artid uniqueidentifier declare @schematype int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematext nvarchar(2000) declare @rcount int set nocount on /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) if sessionproperty('replication_agent') <> 0 begin -- called from Merge Agent. If we are republishing, must add this command for -- any publications which are republishing data from the original publication. declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid from sysmergepublications p where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and exists (select * from sysmergearticles a1, sysmergearticles a2 where a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid) open #per_publication fetch #per_publication into @pubid2 while @@fetch_status<>-1 begin select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 set @schemaguid = newid() set @artid = newid() set @schematype = 16 /* metadata cleanup */ select @schematext = 'exec dbo.sp_MScleanup_metadata '+ '''' + convert(nchar(36),@pubid2) + '''' exec @retcode=sp_MSinsertschemachange @pubid2, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 begin close #per_publication deallocate #per_publication return (1) end fetch next from #per_publication into @pubid2 end close #per_publication deallocate #per_publication end else begin -- should be nested call from sp_mergecleanupmetadata and we should be the -- publisher of this publication if @pubid is not null and not exists (select * from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and pubid = @pubid) begin return (1) end end -- Figure out whether there are any publications which prevent us from truncating the tables if @pubid is null begin if exists (select * from sysmergepublications where LOWER(publisher)<> LOWER(@@SERVERNAME) or publisher_db<>db_name()) set @truncate_flag = 0 else set @truncate_flag = 1 end else begin if exists (select * from sysmergepublications p where p.pubid <> @pubid and (LOWER(publisher)<> LOWER(@@SERVERNAME) or publisher_db<>db_name() or not exists (select * from sysmergearticles a1, sysmergearticles a2 where a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid))) set @truncate_flag = 0 else set @truncate_flag = 1 end if @pubid is not null begin /* If there are any inprocess generations inform user that queisce did not succeed - reinit required */ if exists (select top 1 guidsrc from dbo.MSmerge_genhistory h where guidlocal='00000000-0000-0000-0000-000000000000' -- incomplete gen and generation not in (select gen_cur from sysmergearticles) -- not a local incomplete gen and coldate in (select login_time from master..sysprocesses) -- not a gen that currently receives replica updates from another db and h.art_nick in (select nickname from sysmergearticles where pubid = @pubid)) -- generations relevant to current publication begin declare @dbname sysname set @dbname = db_name() RAISERROR(21504, 16, -1, @@servername, @dbname) return (1) end end if @truncate_flag = 1 begin truncate table MSmerge_contents truncate table MSmerge_tombstone truncate table MSmerge_genhistory update sysmergearticles set gen_cur = NULL update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL end else begin -- set up temp table of article nicknames that we delete for create table #artnicks (nickname int) if @pubid is null begin -- insert pubids for all local publications insert into #artnicks select distinct nickname from sysmergearticles where pubid in (select pubid from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()) end else begin insert into #artnicks select distinct nickname from sysmergearticles where pubid = @pubid end -- do deletions in batches of 5000 to avoid excessive log growth set rowcount 5000 set @rcount = 1 while @rcount > 0 begin set @rcount = 0 -- update our cumulative count of rows deleted in this pass with each table delete MSmerge_contents WITH (PAGLOCK) from MSmerge_contents where tablenick in (select nickname from #artnicks) set @rcount = @@rowcount + @rcount delete MSmerge_tombstone WITH (PAGLOCK) from MSmerge_tombstone where tablenick in (select nickname from #artnicks) set @rcount = @@rowcount + @rcount delete MSmerge_genhistory WITH (PAGLOCK) from MSmerge_genhistory where art_nick in (select nickname from #artnicks) set @rcount = @@rowcount + @rcount end update sysmergearticles set gen_cur = NULL where nickname in (select nickname from #artnicks) if @pubid is not null update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL where repid in ( select subid from sysmergesubscriptions where pubid = @pubid) else update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL where repid in ( select subid from sysmergesubscriptions s, sysmergepublications p where s.pubid = p.pubid and LOWER(p.publisher)=LOWER(@@SERVERNAME) and p.publisher_db=db_name()) drop table #artnicks dbcc dbreindex ( MSmerge_contents ) end execute @retcode = dbo.sp_MSmakegeneration if @@ERROR <> 0 or @retcode <> 0 return (1) return 0 GO exec dbo.sp_MS_marksystemobject sp_MScleanup_metadata go grant execute on dbo.sp_MScleanup_metadata to public go raiserror('Creating procedure sp_helpmergecleanupwait', 0,1) GO create procedure sp_helpmergecleanupwait AS -- Tell user if we don't think we are waiting! if not exists (select * from sysmergesubscriptions where status = 7) raiserror(21507, 0,1) if exists (select * from sysmergepublications p, sysmergesubscriptions s where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and p.pubid = s.pubid and s.status = 7 and s.subid <> s.pubid) begin raiserror(21509, 0,1) select publication, srvid, db_name from sysmergesubscriptions where status = 7 end else if exists (select * from MSmerge_genhistory, master..sysprocesses where guidlocal = '00000000-0000-0000-0000-000000000000' and coldate = login_time) begin raiserror(21508, 0,1) select hostname, program_name from master..sysprocesses where login_time in (select coldate from MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000') end else exec sp_MSquiescecheck GO grant execute on sp_helpmergecleanupwait to public GO exec dbo.sp_MS_marksystemobject sp_helpmergecleanupwait go raiserror('Creating procedure sp_MSquiescecheck', 0,1) GO create procedure sp_MSquiescecheck AS declare @mysrvid int declare @pubid uniqueidentifier declare @retcode int set nocount on -- If we are not waiting to quiesce, do nothing more if not exists (select * from sysmergesubscriptions where status = 7) return 0 -- If we publish to any republishers who are still waiting to -- quiesce (status = 7) then just return for now. if exists (select * from sysmergepublications p, sysmergesubscriptions s where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and p.pubid = s.pubid and s.status = 7 and s.subid <> s.pubid) return 0 -- Set publication status to inactive for all publications I have that are participating -- in the metadata cleanup. update sysmergepublications set status = 0 where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and pubid in (select a1.pubid from sysmergearticles a1, sysmergearticles a2, sysmergesubscriptions s where a1.objid = a2.objid and a2.pubid = s.pubid and s.status = 7) -- If there are any open generations with more than 0 rows, we must still wait if exists (select * from MSmerge_genhistory gh where guidlocal = '00000000-0000-0000-0000-000000000000' and generation not in (select gen_cur from sysmergearticles) and coldate in (select login_time from master..sysprocesses)) return 0 -- Am I the top-level publisher or just a republisher? set @mysrvid = 0 select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME if exists (select * from sysmergesubscriptions s where srvid = @mysrvid and db_name = db_name() and status = 7 and subid <> pubid) begin -- I am not the top level publisher. Just set my status & let it propagate up. update sysmergesubscriptions set status = 8 where srvid = @mysrvid and db_name = db_name() and status = 7 return 0 end else begin -- I am the top level publisher! Start getting rid of that old metadata! select @pubid = pubid from sysmergesubscriptions where srvid = @mysrvid and db_name = db_name() and status = 7 exec @retcode = dbo.sp_MScompletecleanup @pubid return @retcode end GO exec dbo.sp_MS_marksystemobject sp_MSquiescecheck go grant execute on dbo.sp_MSquiescecheck to public go raiserror('Creating procedure sp_mergepreparecleanup', 0,1) GO create procedure sp_mergepreparecleanup @publication sysname AS declare @pubid uniqueidentifier declare @retcode int declare @schematype int declare @schematext nvarchar(2000) /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) set nocount on /* Validate the publication */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end if exists (select * from sysmergearticles a1, sysmergearticles a2 where a1.pubid = @pubid and a2.objid = a1.objid and a2.pubid not in (select pubid from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())) begin raiserror(21272, 16, -1, @publication) return (1) end exec @retcode=sp_MSBumpupCompLevel @pubid, 40 if @@ERROR<>0 or @retcode<>0 return (1) -- stop local updates by creating the quiesce triggers exec @retcode = sp_MSquiescetriggerson -- use publisher row in sysmergesubscriptions to store datetime we started quiesce sequence update sysmergesubscriptions set last_sync_date = getdate() where pubid = @pubid and subid = @pubid -- insert a schema change that will cause subscribers and republishers to prepare for cleanup set @schematype = 17 /* metadata cleanup */ set @schematext = 'exec dbo.sp_MSpreparecleanup ' exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype GO exec dbo.sp_MS_marksystemobject sp_mergepreparecleanup go grant execute on dbo.sp_mergepreparecleanup to public go raiserror('Creating procedure sp_MSpreparecleanup', 0,1) GO create procedure sp_MSpreparecleanup @pubid uniqueidentifier AS declare @retcode int declare @schematype int declare @schematext nvarchar(2000) -- This should only be invoked by the merge agent as it processes schema changes if sessionproperty('replication_agent') = 0 return (1) set nocount on exec @retcode=sp_MSBumpupCompLevel @pubid, 40 if @@ERROR<>0 or @retcode<>0 return (1) -- stop local updates by creating the quiesce triggers exec @retcode = sp_MSquiescetriggerson -- If we are a republisher, propagate the schema change on to our subscribers set @schematype = 17 /* prepare cleanup */ select @schematext = 'exec dbo.sp_MSpreparecleanup ' exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype return (0) GO exec dbo.sp_MS_marksystemobject sp_MSpreparecleanup go grant execute on dbo.sp_MSpreparecleanup to public go raiserror('Creating procedure sp_MSquiescetriggerson', 0,1) GO create procedure sp_MSquiescetriggerson AS declare @cmd nvarchar(4000) if not exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger') begin set @cmd = 'create trigger MS_mergequiescetrigger on MSmerge_contents for UPDATE, INSERT, DELETE NOT FOR REPLICATION AS if sessionproperty(''replication_agent'') = 0 begin -- Raise an appropriate error RAISERROR(21510, 11, -1) rollback tran end' execute (@cmd) end if not exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger2') begin set @cmd = 'create trigger MS_mergequiescetrigger2 on MSmerge_tombstone for UPDATE, INSERT, DELETE NOT FOR REPLICATION AS if sessionproperty(''replication_agent'') = 0 begin -- Raise an appropriate error RAISERROR(21510, 11, -1) rollback tran end' execute (@cmd) end GO exec dbo.sp_MS_marksystemobject sp_MSquiescetriggerson go grant execute on dbo.sp_MSquiescetriggerson to public go raiserror('Creating procedure sp_MSquiescetriggersoff', 0,1) GO create procedure sp_MSquiescetriggersoff AS if exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger') drop trigger MS_mergequiescetrigger if exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger2') drop trigger MS_mergequiescetrigger2 GO exec dbo.sp_MS_marksystemobject sp_MSquiescetriggersoff go raiserror('Creating procedure sp_MSquiesceforcleanup', 0,1) GO create procedure sp_MSquiesceforcleanup @pubid uniqueidentifier AS declare @schematype int declare @schematext nvarchar(4000) declare @mysrvid int declare @retcode int set nocount on set @mysrvid = 0 select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME -- @pubid is normally the publication I subscribe to, get the pubid's of what I republish create table #mypubids(pubid uniqueidentifier) insert into #mypubids (pubid) select distinct p.pubid from sysmergepublications p, sysmergearticles a1, sysmergearticles a2 where a1.pubid = @pubid and a1.objid = a2.objid and p.pubid = a2.pubid and LOWER(p.publisher)=LOWER(@@SERVERNAME) and p.publisher_db=db_name() -- If I don't republish, then I am done and ready if @@ROWCOUNT = 0 begin drop table #mypubids -- REPLICA_STATUS_ReadyForCleanup = 0x0008 update sysmergesubscriptions set status = 8 where pubid = @pubid and db_name = db_name() and srvid = @mysrvid return 0 end -- set my status to QuiesceRequested -- REPLICA_STATUS_QuieseRequested = 0x0007, update sysmergesubscriptions set status = 7 where pubid = @pubid and db_name = db_name() and srvid = @mysrvid update sysmergesubscriptions set status = 7 where pubid in (select pubid from #mypubids) and db_name = db_name() and srvid = @mysrvid -- update status for my republishing subscribers update sysmergesubscriptions set status = 7 where pubid in (select pubid from #mypubids) and subid in (select s1.subid from sysmergesubscriptions s1, sysmergesubscriptions s2 where s1.pubid in (select pubid from #mypubids) and s1.srvid = s2.srvid and s1.db_name = s2.db_name and s2.subid = s2.pubid and s2.status = 1) -- check if any of my subscribers republish if @@ROWCOUNT = 0 begin -- If none of my subscribers are republishers, then I just -- set my publications' status to unavailable and set -- set my replica status as ReadyForCleanup update sysmergepublications set status = 0 where pubid in (select pubid from #mypubids) -- I may have ongoing merges call the quiescecheck routine to try and set my status exec @retcode = sp_MSquiescecheck end else begin -- propagate the schema change on to my subscribers set @schematype = 19 -- SCHEMA_TYPE_QUIESCE = 19 set @schematext = 'exec dbo.sp_MSquiesceforcleanup ' exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype end drop table #mypubids GO grant execute on dbo.sp_MSquiesceforcleanup to public go exec dbo.sp_MS_marksystemobject sp_MSquiesceforcleanup go raiserror('Creating procedure sp_mergecompletecleanup', 0,1) GO create procedure sp_mergecompletecleanup @publication sysname AS declare @pubid uniqueidentifier declare @retcode int set nocount on /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* Validate the publication */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end -- Make sure we are at the top-level publisher if exists (select * from sysmergearticles a1, sysmergearticles a2 where a1.pubid = @pubid and a2.objid = a1.objid and a2.pubid not in (select pubid from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())) begin raiserror(21272, 16, -1, @publication) return (1) end -- If user never id sp_mergepreparecleanup then inform them of their error. if not exists(select * from sysmergeschemachange where schematype = 17) begin raiserror(21506, 0, 1) return (1) end -- Use sp_MSquiesceforcleanup to propagate schema, etc. exec @retcode = sp_MSquiesceforcleanup @pubid if @@ERROR<>0 or @retcode<>0 return (1) GO exec dbo.sp_MS_marksystemobject sp_mergecompletecleanup go grant execute on dbo.sp_mergecompletecleanup to public go raiserror('Creating procedure sp_MScompletecleanup', 0,1) GO create procedure sp_MScompletecleanup @pubid uniqueidentifier AS declare @retcode int declare @otherpubs int declare @pubid2 uniqueidentifier declare @artid uniqueidentifier declare @schematype int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematext nvarchar(2000) declare @rcount int declare @dtprepare datetime declare @cutoffgen int declare @mysrvid int declare @toplevel int set nocount on if exists (select * from sysmergepublications where pubid = @pubid and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()) set @toplevel = 1 else set @toplevel = 0 /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) -- Figure out whether there are any other publications which make us copy additional rows set @otherpubs = 0 if exists (select * from sysmergearticles a1 where a1.objid not in (select objid from sysmergearticles where pubid = @pubid)) set @otherpubs = 1 if @otherpubs = 1 or @toplevel = 1 begin -- create some temp tables to preserve part of MSmerge_contents create table #contents (tablenick int NOT NULL, rowguid uniqueidentifier rowguidcol NOT NULL, generation int NOT NULL, partchangegen int NULL, joinchangegen int NULL, lineage varbinary(249) NOT NULL, colv1 varbinary(2048) NULL) create table #tombstone (rowguid uniqueidentifier rowguidcol NOT NULL, tablenick int NOT NULL, type tinyint NOT NULL, lineage varbinary(249) NOT NULL, generation int NOT NULL, reason nvarchar(255) NOT NULL) end -- propagate the schema change on to our subscribers set @schematype = 18 /* metadata cleanup */ select @schematext = 'exec dbo.sp_MScompletecleanup ' exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype if @toplevel = 1 begin /* Get the timestamp when we did the prepareforcleanup */ select @dtprepare = last_sync_date from sysmergesubscriptions where pubid = @pubid and subid = @pubid -- Copy some generations from contents and tombstone into temptables select @cutoffgen = min(generation) from MSmerge_genhistory where coldate > @dtprepare if @otherpubs = 1 begin insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1) select tablenick, rowguid, 0, case when partchangegen < @cutoffgen then NULL else 0 end, case when joinchangegen < @cutoffgen then NULL else 0 end, lineage, colv1 from MSmerge_contents where generation >= @cutoffgen and tablenick in (select nickname from sysmergearticles where pubid = @pubid) end else begin -- leaving out join to sysmergearticles should help performance insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1) select tablenick, rowguid, 0, case when partchangegen < @cutoffgen then NULL else 0 end, case when joinchangegen < @cutoffgen then NULL else 0 end, lineage, colv1 from MSmerge_contents where generation >= @cutoffgen end if @otherpubs = 1 begin insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason) select rowguid, tablenick, type, lineage, 0, reason from MSmerge_tombstone where generation >= @cutoffgen and tablenick in (select nickname from sysmergearticles where pubid = @pubid) end else begin insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason) select rowguid, tablenick, type, lineage, 0, reason from MSmerge_tombstone where generation >= @cutoffgen end end if @otherpubs = 1 begin insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1) select * from MSmerge_contents where tablenick not in (select nickname from sysmergearticles where pubid = @pubid) insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason) select * from MSmerge_tombstone where tablenick not in (select nickname from sysmergearticles where pubid = @pubid) end truncate table MSmerge_contents truncate table MSmerge_tombstone if @otherpubs = 0 begin truncate table MSmerge_genhistory update sysmergearticles set gen_cur = NULL update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL end else begin -- set up temp table of article nicknames that we delete for create table #artnicks (nickname int) insert into #artnicks select distinct nickname from sysmergearticles where pubid = @pubid -- do deletions in batches of 5000 to avoid excessive log growth set rowcount 5000 set @rcount = 1 while @rcount > 0 begin set @rcount = 0 delete MSmerge_genhistory WITH (PAGLOCK) from MSmerge_genhistory where art_nick in (select nickname from #artnicks) set @rcount = @@rowcount end update sysmergearticles set gen_cur = NULL where nickname in (select nickname from #artnicks) update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL where repid in ( select subid from sysmergesubscriptions where pubid = @pubid) drop table #artnicks end -- Remove triggers now so we can reinsert the rows we saved from contents execute @retcode = sp_MSquiescetriggersoff if @toplevel = 1 or @otherpubs = 1 begin -- Put back the rows from #contents and #tombstone insert into MSmerge_contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1) select * from #contents insert into MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) select * from #tombstone end -- Mark publications as active but snapshot expired update sysmergepublications set status = 1, snapshot_ready = 3 where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and pubid in (select a1.pubid from sysmergearticles a1, sysmergearticles a2 where a2.pubid = @pubid and a1.objid = a2.objid) -- Set our subscriber status back to 1 set @mysrvid = 0 select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME update sysmergesubscriptions set status = 1 where srvid = @mysrvid and db_name = db_name() and pubid in (select a1.pubid from sysmergearticles a1, sysmergearticles a2 where a1.objid = a2.objid and a2.pubid = @pubid) -- update subscriptions rows that still say status of ReadyForCleanup update sysmergesubscriptions set status = 1 where status = 8 execute @retcode = dbo.sp_MSmakegeneration if @@ERROR <> 0 or @retcode <> 0 return (1) return 0 GO exec dbo.sp_MS_marksystemobject sp_MScompletecleanup go raiserror('Creating procedure sp_MSpropagateschematorepubs', 0,1) GO create procedure sp_MSpropagateschematorepubs @pubid uniqueidentifier, @schema_text nvarchar(4000), @schema_type int AS declare @pubid2 uniqueidentifier declare @artid uniqueidentifier declare @fulltext nvarchar(4000) declare @retcode int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematext nvarchar(2000) set nocount on -- Find republications and add the same schema change declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid from sysmergepublications p where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and exists (select * from sysmergearticles a1, sysmergearticles a2 where a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid) open #per_publication fetch #per_publication into @pubid2 while @@fetch_status<>-1 begin select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 set @schemaguid = newid() set @artid = newid() select @fulltext = @schema_text + ' ''' + convert(nchar(36),@pubid2) + '''' exec @retcode=sp_MSinsertschemachange @pubid2, @artid, @schemaversion, @schemaguid, @schema_type, @fulltext if @@ERROR<>0 or @retcode<>0 begin close #per_publication deallocate #per_publication return (1) end fetch next from #per_publication into @pubid2 end close #per_publication deallocate #per_publication return (0) GO exec dbo.sp_MS_marksystemobject sp_MSpropagateschematorepubs go raiserror('Creating procedure sp_mergecleanupmetadata', 0,1) GO create procedure sp_mergecleanupmetadata @publication sysname = '%', @reinitialize_subscriber nvarchar(5) = 'true' AS declare @pubid uniqueidentifier declare @retcode int declare @pubname sysname declare @snapshot_ready int declare @make_generation int declare @artid uniqueidentifier declare @schematype int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematext nvarchar(2000) set @make_generation = 0 /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) /* Validate the publication name if one is provided */ if @publication <> '%' begin select @pubid = pubid from sysmergepublications where name = @publication if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end end if not exists (select * from sysobjects where name='sysmergearticles') return (1) begin TRAN save TRAN cleanupmetadata declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and name like @publication open #per_publication fetch #per_publication into @pubid while @@fetch_status<>-1 begin select @pubname=name, @snapshot_ready=snapshot_ready from sysmergepublications where pubid=@pubid if @publication <> '%' and exists (select * from sysmergearticles where pubid<>@pubid and objid in (select objid from sysmergearticles where pubid=@pubid)) begin raiserror(21272, 16, -1, @pubname) goto FAILURE end /* Only do this when snapshot has been ran before this operation */ if @snapshot_ready>0 begin /* If there are any inprocess generations defer the cleanup of metadata */ if exists (select top 1 guidsrc from dbo.MSmerge_genhistory h where guidlocal='00000000-0000-0000-0000-000000000000' -- incomplete gen and generation not in (select gen_cur from sysmergearticles) -- not a local incomplete gen and coldate in (select login_time from master..sysprocesses) -- not a gen that currently receives replica updates from another db and h.art_nick in (select nickname from sysmergearticles where pubid = @pubid)) -- generations relevant to current publication begin RAISERROR(21503, 16, -1) close #per_publication deallocate #per_publication goto FAILURE end if LOWER(@reinitialize_subscriber collate SQL_Latin1_General_CP1_CS_AS)='true' begin -- backward-comp-level is bump'd up by calling this stored procedure exec @retcode = sp_MSreinitmergepublication @pubname if @@ERROR<>0 or @retcode<>0 begin close #per_publication deallocate #per_publication goto FAILURE end update sysmergepublications set snapshot_ready=2 where pubid=@pubid if @@ERROR<>0 begin close #per_publication deallocate #per_publication goto FAILURE end end else begin raiserror(21355, 10, -1,@publication) exec @retcode=sp_MSBumpupCompLevel @pubid, 30 --SP2 subscribers support this, but do not propagate if @@ERROR<>0 or @retcode<>0 begin close #per_publication deallocate #per_publication goto FAILURE end select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @artid = newid() set @schematype = 16 /* metadata cleanup */ select @schematext = 'exec dbo.sp_MScleanup_metadata '+ '''' + convert(nchar(36),@pubid) + '''' exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 begin close #per_publication deallocate #per_publication goto FAILURE end end end set @make_generation = 1 fetch next from #per_publication into @pubid end close #per_publication deallocate #per_publication COMMIT TRAN if @publication = '%' begin set @pubid = NULL end else begin select @pubid = pubid from sysmergepublications where name = @publication end exec @retcode = dbo.sp_MScleanup_metadata @pubid if @@ERROR <> 0 or @retcode <> 0 goto FAILURE if @make_generation = 1 begin execute @retcode = dbo.sp_MSmakegeneration if @@ERROR <> 0 or @retcode <> 0 goto FAILURE end return (0) FAILURE: if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION cleanupmetadata COMMIT TRANSACTION end return (1) GO exec dbo.sp_MS_marksystemobject sp_mergecleanupmetadata go grant execute on dbo.sp_mergecleanupmetadata to public go raiserror('Creating procedure sp_MScleanup_conflict_table', 0,1) GO /* ** This stored procedure is to periodically check and cleanup all the conflict entries ** in conflict tables that has been there longer than the value of conflict_retention in ** days. */ create procedure sp_MScleanup_conflict_table AS BEGIN declare @retcode int declare @pubid uniqueidentifier declare @conflict_retention int ,@conflict_table sysname ,@cmd nvarchar(4000) ,@tranpubid int /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) -- -- merge cleanup -- if exists (select * from sysobjects where name = 'sysmergepublications') begin declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT pubid, conflict_retention from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and conflict_retention>0 open PC fetch PC into @pubid, @conflict_retention while (@@fetch_status<>-1) begin exec @retcode = sp_MScleanup_conflict @pubid, @conflict_retention if @@ERROR<>0 or @retcode<>0 begin close PC deallocate PC return (1) end fetch next from PC into @pubid, @conflict_retention end close PC deallocate PC end -- -- tran cleanup -- if (EXISTS (select * from sysobjects where name = 'syspublications')) begin -- -- do for each conflict table in each publication -- declare hCftTab cursor LOCAL FAST_FORWARD for select a.pubid, a.conflict_retention, OBJECT_NAME(c.conflict_tableid) from (syspublications as a join sysarticles as b on a.pubid = b.pubid) join sysarticleupdates as c on c.artid = b.artid and c.pubid = b.pubid where a.allow_queued_tran = 1 and a.conflict_retention>0 open hCftTab fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table while (@@fetch_status != -1) begin -- -- delete the expired messages -- select @cmd = 'delete ' + quotename(master.dbo.fn_MSgensqescstr(@conflict_table)) collate database_default + ' where datediff(dd, getdate(), insertdate) > ' + cast(@conflict_retention as nvarchar(10)) + ' and pubid = ' + cast(@tranpubid as nvarchar(10)) execute (@cmd) if (@@error != 0) begin close hCftTab deallocate hCftTab return 1 end -- -- Get next conflict table to clean -- fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table end -- -- close cursor -- close hCftTab deallocate hCftTab end -- -- all done -- return 0 END GO exec dbo.sp_MS_marksystemobject sp_MScleanup_conflict_table go raiserror('Creating procedure sp_validatemergesubscription', 0,1) GO CREATE PROCEDURE sp_validatemergesubscription (@publication sysname, @subscriber sysname, @subscriber_db sysname, @level tinyint ) AS declare @subscriber_srvid int declare @retcode int declare @pubid uniqueidentifier /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end if @level <1 or @level > 3 begin raiserror(21184, 16, -1, '@level', '1', '2','3') return (1) end if not exists (select * from sysmergesubscriptions where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid) begin raiserror(14055, 16, -1) return (1) end update sysmergesubscriptions set validation_level=@level where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid if @@ERROR<>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_validatemergesubscription go grant exec on dbo.sp_validatemergesubscription to public go raiserror('Creating procedure sp_validatemergepublication', 0,1) GO CREATE PROCEDURE sp_validatemergepublication (@publication sysname, @level tinyint ) AS declare @artid uniqueidentifier declare @schematype int declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematext nvarchar declare @retcode int declare @pubid uniqueidentifier /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* make sure current database is enabled for merge replication */ exec @retcode=dbo.sp_MSCheckmergereplication if @@ERROR<>0 or @retcode<>0 return (1) select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end if @level <1 or @level > 3 begin raiserror(21184, 16, -1, '@level', '1', '2','3') return (1) end raiserror(21356, 10, -1, @publication) exec @retcode=sp_MSBumpupCompLevel @pubid, 30 if @@ERROR<>0 or @retcode<>0 return (1) select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange set @schemaguid = newid() set @artid = newid() set @schematype = 66 /* publication wide validation */ select @schematext = convert(nvarchar, @level) exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@ERROR<>0 or @retcode<>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_validatemergepublication go grant exec on dbo.sp_validatemergepublication to public go raiserror('Creating procedure sp_MScleanup_conflict', 0,1) GO create procedure sp_MScleanup_conflict @pubid uniqueidentifier, @conflict_retention int = NULL AS declare @pubname sysname declare @valid_date datetime declare @conflict_table sysname declare @conflict_id int declare @retention_string nvarchar(100) declare @pubidstr nvarchar(100) declare @retcode int --if no conflict_retention value is specified, query local table and find out. if @conflict_retention is NULL select @conflict_retention=conflict_retention from sysmergepublications where pubid=@pubid --do not do any cleanup if conflict retention value is 0 else if @conflict_retention = 0 return (0) select @pubname=name from sysmergepublications where pubid=@pubid select @pubidstr = convert(nvarchar(40), @pubid) select @valid_date = dateadd(day, -@conflict_retention, getdate()) select @retention_string = convert(nvarchar, @conflict_retention) /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) if exists (select name from syscolumns where name='create_time' and id = object_id('MSmerge_delete_conflicts')) begin delete from MSmerge_delete_conflicts where create_time < @valid_date and pubid=@pubid if @@ERROR<>0 return (1) end declare AC CURSOR LOCAL FAST_FORWARD for select conflict_table from sysmergearticles where pubid=@pubid open AC fetch AC into @conflict_table while (@@fetch_status<>-1) begin if @conflict_table is NOT null begin select @conflict_id = object_id(@conflict_table) select @conflict_table=QUOTENAME(@conflict_table) /* ** Upgrade conflict table so that it can get cleaned up later on */ if @conflict_id is not NULL and not exists (select name from syscolumns where name='MSrepl_create_time' and id=@conflict_id) begin exec ('alter table ' + @conflict_table + ' add MSrepl_create_time datetime not NULL default getdate() ') if @@ERROR<>0 goto FAILURE end if @conflict_id is not NULL and @retention_string is not NULL and exists (select name from syscolumns where name='MSrepl_create_time' and id=@conflict_id) begin exec ('delete from ' + @conflict_table + ' where datediff(dd, MSrepl_create_time, getdate()) > ' + @retention_string + ' and pubid = ''' + @pubidstr + '''') if @@ERROR<>0 goto FAILURE end end fetch next from AC into @conflict_table end close AC deallocate AC return (0) FAILURE: close AC deallocate AC return (1) GO exec dbo.sp_MS_marksystemobject sp_MScleanup_conflict go raiserror('Creating procedure sp_generatefilters', 0,1) GO create procedure sp_generatefilters @publication sysname AS /* Declare a few variables */ declare @pubid uniqueidentifier declare @art_name sysname declare @object_id int declare @join_objid int declare @retcode int declare @join_unique int declare @distance int /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Parameter Check: @publication. ** The @publication id cannot be NULL and must conform to the rules ** for identifiers. */ if @publication is NULL begin raiserror (14043, 16, -1, '@publication') return (1) end /* ** Get the pubid and make sure the publication exists */ select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 16, -1, @publication) return (1) end /* Set up some temp tables to help keep track of what to process */ create table #filtered (object_id int NOT NULL, distance int NOT NULL) create table #unfiltered (object_id int NOT NULL, art_name sysname collate database_default not null) /* Do initial population of temp tables */ insert into #filtered (object_id, distance) select objid, 0 from sysmergearticles where pubid = @pubid and len(subset_filterclause) > 0 insert into #unfiltered (object_id, art_name) select objid, name from sysmergearticles where pubid = @pubid and objid not in (select object_id from #filtered) /* remove self-referencing tables from #unfiltered as we should not try to filter them */ delete from #unfiltered where object_id in (select rkeyid from sysreferences where rkeyid = fkeyid) select @distance = min(distance) from #filtered f, sysreferences r, #unfiltered u where (f.object_id = r.rkeyid and r.fkeyid = u.object_id) or (u.object_id = r.rkeyid and r.fkeyid = f.object_id) /* Look for something in sysreferences to add a join filter for */ select @join_unique = 1, @object_id = fkeyid, @join_objid = rkeyid, @art_name = art_name from sysreferences r, #unfiltered u where r.fkeyid = u.object_id and r.rkeyid in (select object_id from #filtered where distance = @distance) if @art_name is null select @join_unique = 0, @object_id = rkeyid, @join_objid = fkeyid, @art_name = art_name from sysreferences r, #unfiltered u where r.rkeyid = u.object_id and r.fkeyid in (select object_id from #filtered where distance = @distance) while @art_name is not null begin /* Make the join filter corresponding to this relationship */ exec @retcode=sp_MSmakejoinfilter @publication, @art_name, @object_id, @join_objid, @join_unique if @@ERROR<>0 or @retcode<>0 return (1) /* Move row from #unfiltered to #filtered */ insert into #filtered (object_id, distance) values (@object_id, @distance + 1) delete from #unfiltered where object_id = @object_id /* See if any more that can be added */ select @distance = min(distance) from #filtered f, sysreferences r, #unfiltered u where (f.object_id = r.rkeyid and r.fkeyid = u.object_id) or (u.object_id = r.rkeyid and r.fkeyid = f.object_id) set @art_name = NULL select @join_unique = 1, @object_id = fkeyid, @join_objid = rkeyid, @art_name = art_name from sysreferences r, #unfiltered u where r.fkeyid = u.object_id and r.rkeyid in (select object_id from #filtered where distance = @distance) if @art_name is null select @join_unique = 0, @object_id = rkeyid, @join_objid = fkeyid, @art_name = art_name from sysreferences r, #unfiltered u where r.rkeyid = u.object_id and r.fkeyid in (select object_id from #filtered where distance = @distance) end return (0) go exec dbo.sp_MS_marksystemobject sp_generatefilters go grant execute on dbo.sp_generatefilters to public go /* ** Name : sp_MShelpmergeconflictcounts ** Description: This sp returns the count of conflicts (from MSmerge_delete_conflicts and ** each conflict table) in each publication. Results can optionally be filtered ** to include only a single publication. Results are always ordered by article ** name. Only articles with non-zero conflict counts are returned. ** Parameters: 1. Publication Name( sysname; default '%'==ALL PUBLICATIONS) ** Output Result Set has the following structure ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. article (sysname) Article name ** b. conflict_table (sysname) Associated conflict table ** c. guidcolname (sysname) Article's rowguidcol name ** d. centralized_conflicts(integer) Centralized (1) or Decentralized (0) ** conflicts specified by the article ** e. conflict_ucount (integer) Count of (update) conflicts in the ** conflict table for this article ** f. conflicts_dcount (integer) Count of (delete) conflicts in the ** MSmerge_delete_conflicts table for this article */ raiserror('Creating procedure sp_MShelpmergeconflictcounts', 0,1) GO create procedure sp_MShelpmergeconflictcounts ( @publication_name sysname = '%' , @publisher sysname = NULL, @publisher_db sysname = NULL) as begin set nocount on declare @aname sysname declare @cmd nvarchar(2000) declare @conflict_table sysname declare @count integer declare @pubid uniqueidentifier declare @pubidstr nvarchar(38) if @publisher IS NULL select @publisher = @@SERVERNAME if @publisher_db IS NULL select @publisher_db = db_name() if @publication_name <> '%' begin /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ select @pubid = pubid from sysmergepublications where name = @publication_name and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication_name) RETURN (1) END set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' end -- make sure current db has merge publishing tables (true on both pub and sub) if not exists ( select * from sysobjects where name = 'sysmergearticles') begin raiserror( 18757, 16, -1 ) return(1) end -- allow null conflict table name to handle case where there are delete conflicts but no update conflicts create table #result_list ( article sysname collate database_default, source_object sysname collate database_default, conflict_table sysname collate database_default null, guidcolname sysname collate database_default, centralized_conflicts integer, conflicts_ucount integer, conflicts_dcount integer ) create table #conflict_list ( article_name sysname collate database_default, conflicts_ucount integer, conflicts_dcount integer ) create table #update_list ( article_name sysname collate database_default, conflicts_ucount integer ) -- get delete counts if ( @publication_name = '%' ) declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for select distinct a.name from MSmerge_delete_conflicts d inner join sysmergepublications p on p.pubid = d.pubid inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick else declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for select distinct a.name from MSmerge_delete_conflicts d inner join sysmergepublications p on p.pubid = d.pubid inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick where p.pubid = @pubid open hCdcount fetch hCdcount into @aname while ( @@fetch_status <> -1 ) begin select @cmd = 'select N''' + @aname + ''', 0, count(*) from MSmerge_delete_conflicts d inner join sysmergepublications p on p.pubid = d.pubid inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick where a.name = N''' + @aname + ''' ' if ( @publication_name <> '%' ) select @cmd = @cmd + ' and p.pubid = ' + @pubidstr insert #conflict_list ( article_name, conflicts_ucount, conflicts_dcount ) exec ( @cmd ) fetch hCdcount into @aname end close hCdcount deallocate hCdcount -- get update counts if ( @publication_name = '%' ) declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for select distinct a.name, a.conflict_table from sysmergepublications p inner join sysmergearticles a on a.pubid = p.pubid where a.conflict_table is not null else declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for select distinct a.name, a.conflict_table from sysmergepublications p inner join sysmergearticles a on a.pubid = p.pubid where a.conflict_table is not null and p.pubid = @pubid open hCucount fetch hCucount into @aname, @conflict_table while ( @@fetch_status <> -1 ) begin select @cmd = N'select N''' + @aname + N''', count(*) from ' + QUOTENAME( @conflict_table ) + N' ct inner join sysmergepublications p on p.pubid = ct.pubid ' if ( @publication_name <> '%' ) select @cmd = @cmd + ' where p.name = N''' + @publication_name + '''' insert #update_list ( article_name, conflicts_ucount ) exec( @cmd ) fetch hCucount into @aname, @conflict_table end close hCucount deallocate hCucount update #conflict_list set conflicts_ucount = isnull( ul.conflicts_ucount, 0 ) from #conflict_list cl inner join #update_list ul on ul.article_name = cl.article_name delete #update_list from #update_list ul inner join #conflict_list cl on ul.article_name = cl.article_name insert #conflict_list select *, 0 from #update_list where conflicts_ucount > 0 drop table #update_list select @cmd = N'select distinct t.article_name, N'''' + quotename(user_name( o.uid )) + ''.'' + quotename(o.name) + '''',' + ' a.conflict_table, c.name, p.centralized_conflicts, t.conflicts_ucount, t.conflicts_dcount from #conflict_list t inner join sysmergearticles a on a.name = t.article_name inner join sysmergepublications p on p.pubid = a.pubid inner join sysobjects o on o.id = a.objid inner join syscolumns c on c.id = o.id and ColumnProperty (o.id, c.name, ''IsRowGuidCol'') = 1 where (t.conflicts_ucount > 0 or t.conflicts_dcount > 0) and a.conflict_table is NOT NULL' insert #result_list exec ( @cmd ) if ( @@error <> 0 ) return (1) drop table #conflict_list select * from #result_list drop table #result_list return (0) end go exec dbo.sp_MS_marksystemobject sp_MShelpmergeconflictcounts go grant execute on dbo.sp_MShelpmergeconflictcounts to public go /* ** Name : sp_MShelpmergeconflictpublications ** Description: This sp returns a list of merge publications in the current database ** that may have conflicts. ** Results are always ordered by publication name. ** Output Result Set has the following structure ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. publication (sysname) Publication name ** b. merge_pub_id (uniqueidentifier) Merge publication id */ raiserror('Creating procedure sp_MShelpmergeconflictcpublications', 0,1) GO create procedure sp_MShelpmergeconflictpublications as begin set nocount on declare @dbname sysname, @cmd nvarchar(200) set @dbname = db_name() -- make sure current db has merge publishing tables (true on both pub and sub) if not exists ( select * from sysobjects where name = 'sysmergepublications') begin raiserror( 18757, 16, -1 ) return(1) end -- unlike tran, all merge publications are updateable, so just return all set @cmd = 'select name, pubid, publisher, publisher_db from ' + @dbname + '.dbo.sysmergepublications order by name' exec( @cmd ) return (0) end go exec dbo.sp_MS_marksystemobject sp_MShelpmergeconflictpublications go grant execute on dbo.sp_MShelpmergeconflictpublications to public go create procedure sp_MSclearcolumnbit @pubid uniqueidentifier, @artid uniqueidentifier, @column_name sysname as declare @tablename sysname declare @colid int declare @in_use bit declare @objid int declare @columns varbinary(128) declare @local_repid uniqueidentifier declare @publish int set nocount on set @publish = 0x4000 set @in_use=0 set @local_repid = NULL select @tablename = object_name(objid), @objid=objid from sysmergearticles where artid=@artid and pubid=@pubid select @colid=colid from syscolumns where id=@objid and name=@column_name select top 1 @local_repid=pubid from sysmergearticles where artid=@artid and pubid<>@pubid order by pubid ASC if @local_repid is NULL begin update syscolumns set colstat = colstat & ~@publish where id=@objid and colid=@colid if @@ERROR<>0 return (1) return (0) end while @local_repid is not NULL and @in_use=0 begin select @columns = columns from sysmergearticles where pubid=@local_repid and artid=@artid exec @in_use = sp_MStestbit @bm=@columns, @coltotest=@colid set @local_repid = NULL select top 1 @local_repid=pubid from sysmergearticles where artid=@artid and pubid>@local_repid order by pubid ASC end if @in_use=0 begin update syscolumns set colstat = colstat & ~@publish where id=@objid and colid=@colid if @@ERROR<>0 return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSclearcolumnbit go grant execute on dbo.sp_MSclearcolumnbit to public go /* ** Name : sp_helpmergearticleconflicts ** Description: This sp returns the articles in the publication that have conflicts. ** Optionally if the publication is not specified, all articles in the ** database that have conflicts is returned. ** Parameters: Publication Name( default NULL) ** Output Result Set has the following columns ** publication, article, source_object, conflict_table, guidcolname */ raiserror('Creating procedure sp_helpmergearticleconflicts', 0,1) GO CREATE PROCEDURE sp_helpmergearticleconflicts( @publication sysname = '%', @publisher sysname = NULL, @publisher_db sysname = NULL ) as set nocount on declare @pubid uniqueidentifier declare @cmd nvarchar(4000) declare @retcode int declare @nickname int declare @retcode2 int declare @name sysname declare @source_owner sysname declare @source_object sysname declare @conflict_table sysname declare @guidcolname sysname declare @centralized_conflicts int declare @objid int declare @command nvarchar(200) if @publisher IS NULL select @publisher = @@SERVERNAME if @publisher_db IS NULL select @publisher_db = db_name() if @publication <> '%' begin /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END end create table #temp_conflict(article sysname collate database_default, source_owner sysname collate database_default, source_object sysname collate database_default, conflict_table sysname collate database_default, guidcolname sysname collate database_default, centralized_conflicts int) declare #cur_conflict cursor local for select name, objid, conflict_table, pubid, nickname from sysmergearticles where conflict_table is not NULL and pubid in (select pubid from sysmergepublications where name like @publication) for read only open #cur_conflict fetch #cur_conflict into @name, @objid, @conflict_table, @pubid, @nickname while (@@fetch_status <> -1) begin select @source_owner = user_name(uid) from sysobjects where id = @objid select @source_object = object_name (@objid) select @guidcolname = name from syscolumns where id = @objid and ColumnProperty(@objid, name, 'IsRowGuidCol') = 1 select @centralized_conflicts = centralized_conflicts from sysmergepublications where pubid = @pubid select @command = 'if exists (select * from ' + QUOTENAME(@conflict_table) + ') select @retcode2 = 1 else select @retcode2 = 0' EXEC @retcode = dbo.sp_executesql @command, N'@retcode2 int output', @retcode2 output if @retcode <>0 return (1) if @retcode2 = 1 begin insert into #temp_conflict values (@name, @source_owner, @source_object, @conflict_table, @guidcolname, @centralized_conflicts) end if EXISTS (select * from MSmerge_delete_conflicts where tablenick = @nickname) begin insert into #temp_conflict values (@name, @source_owner, @source_object, 'MSmerge_delete_conflicts', @guidcolname, @centralized_conflicts) end fetch #cur_conflict into @name, @objid, @conflict_table, @pubid, @nickname end select * from #temp_conflict order by article drop table #temp_conflict close #cur_conflict deallocate #cur_conflict return(0) go exec dbo.sp_MS_marksystemobject sp_helpmergearticleconflicts go /* ** Name : sp_helpmergeconflictrows ** Description: This sp returns the rows in the conflict_table specified. ** Optionally if the publication is specified, all conflicts qualified by the ** publication are returned. For instance if the Conflict_Customers table ** has conflict rows for the 'WA' and the 'CA' publication, passing in ** a publication name say 'CA' retrieves conflicts pertaining to the ** 'CA' publication. ** Parameters: 1. Publication Name( default NULL) ** 2. Conflict Table Name ** Output Result Set has the same structure as the Conflict_<table> i.e the base ** table structure with the following additional columns: ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. origin_datasource (varchar(255)) Indicates the origin of the conflict ** b. conflict_type (int) Code indicating type of conflict ** UpdateConflict = 1 ** UploadError = 2 ** DownloadError = 3 ** UpdateDeleteConflict= 4 ** ColumnUpdateConflict= 5 ** c. reason_code (int) Error code that may be context sensitive ** d. reason_text (varchar(720)) Error description that may be context sensitive ** e. pubid (uniqueidentifier) Publication identifier */ raiserror('Creating procedure sp_helpmergeconflictrows', 0,1) GO CREATE PROCEDURE sp_helpmergeconflictrows( @publication sysname = '%', @conflict_table sysname, @publisher sysname = NULL, @publisher_db sysname = NULL ) as set nocount on declare @pubid uniqueidentifier declare @cmd nvarchar(4000) declare @pubidstr nvarchar(38) if @publisher IS NULL select @publisher = @@SERVERNAME if @publisher_db IS NULL select @publisher_db = db_name() select @cmd = 'select * from ' select @cmd = @cmd + QUOTENAME(@conflict_table) if @publication <> '%' begin /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' select @cmd = @cmd + ' where pubid = ' + @pubidstr end exec (@cmd) if (@@error <> 0) RETURN (1) return 0 go exec dbo.sp_MS_marksystemobject sp_helpmergeconflictrows go /* ** Name : sp_helpmergedeleteconflictrows ** Description: This sp returns the rows in the MSmerge_delete_conflicts specified. ** Optionally if the publication is specified, all conflicts qualified by the ** publication are returned. For instance if the MSmerge_delete_conflicts table ** has conflict rows for the 'WA' and the 'CA' publication, passing in ** a publication name say 'CA' retrieves conflicts pertaining to the ** 'CA' publication only. ** Parameters: 1. Publication Name( default NULL) ** 2. Source Object Name ** Output Result Set has the following structure ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. source_object (nvarchar(386)) Indicates the source object for the delete conflict ** b. rowguid (uniqueidentifier) Row identifier for the delete conflict ** c. origin_datasource (varchar(255)) Indicates the origin of the conflict ** d. conflict_type (int) Code indicating type of conflict ** UpdateConflict = 1 ** UploadError = 2 ** DownloadError = 3 ** UpdateDeleteConflict= 4 ** ColumnUpdateConflict= 5 ** e. reason_code (int) Error code that may be context sensitive ** f. reason_text (varchar(720)) Error description that may be context sensitive ** g. pubid (uniqueidentifier) Publication identifier */ raiserror('Creating procedure sp_helpmergedeleteconflictrows', 0,1) GO CREATE PROCEDURE sp_helpmergedeleteconflictrows( @publication sysname = '%', @source_object nvarchar(386) = NULL, @publisher sysname = NULL, @publisher_db sysname = NULL ) as declare @pubid uniqueidentifier declare @cmd nvarchar(4000) declare @pubidstr nvarchar(38) if @publisher IS NULL select @publisher = @@SERVERNAME if @publisher_db IS NULL select @publisher_db = db_name() select @cmd = 'select distinct source_object = user_name(sysobjects.uid) + ' select @cmd = @cmd + '''.''' select @cmd = @cmd + ' + sysobjects.name, MSmerge_delete_conflicts.rowguid, MSmerge_delete_conflicts.conflict_type, ' select @cmd = @cmd + ' MSmerge_delete_conflicts.reason_code, MSmerge_delete_conflicts.reason_text, ' select @cmd = @cmd + ' MSmerge_delete_conflicts.origin_datasource, MSmerge_delete_conflicts.pubid, MSmerge_delete_conflicts.create_time from MSmerge_delete_conflicts, sysmergearticles, sysobjects' select @cmd = @cmd + ' where sysmergearticles.nickname = MSmerge_delete_conflicts.tablenick and sysobjects.id = sysmergearticles.objid ' if @publication <> '%' begin /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' select @cmd = @cmd + ' and MSmerge_delete_conflicts.pubid = ' + @pubidstr end if @source_object IS NOT NULL begin declare @object sysname declare @owner sysname declare @tablenick int declare @tablenickstr nvarchar(11) select @object = PARSENAME(@source_object, 1) select @owner = PARSENAME(@source_object, 2) execute dbo.sp_MStablenickname @owner, @object, @tablenick output if @tablenick IS NULL BEGIN raiserror (20003, 11, -1, @object) RETURN (1) END set @tablenickstr = convert(nchar, @tablenick) select @cmd = @cmd + ' and MSmerge_delete_conflicts.tablenick = ' select @cmd = @cmd + @tablenickstr end exec (@cmd) if (@@error <> 0) RETURN (1) return 0 go exec dbo.sp_MS_marksystemobject sp_helpmergedeleteconflictrows go /* ** Name : sp_deletemergeconflictrow ** Description: This sp deletes the row matching rowguid and origin_datasource ** If now rows are left in conflict_table ** --Set the conflict_table property of the article(s) to NULL ** --Drop the conflict table (optionally) ** If the conflict_table is specified as NULL, the conflict is assumed ** to be a delete conflict and the row matching rowguid and origin_datasource ** and source_object is deleted from the MSmerge_delete_conflicts table. The ** MSmerge_delete_conflicts table is a system table and is not deleted ** from the database even if it is empty. ** Parameters: ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. conflict_table (sysname) Indicates the conflict table name. If '%' then delete from MSmerge_delete_conflicts ** b. source_object (nvarchar(386)) Source table ** c. rowguid (uniqueidentifier) Row identifier for the delete conflict ** d. origin_datasource (varchar(255)) Indicates the origin of the conflict ** e. drop_table_if_empty (varchar(10)) Flag indicating if the Conflict_<table> ** is to be dropped if is empty */ raiserror('Creating procedure sp_deletemergeconflictrow', 0,1) GO CREATE PROCEDURE sp_deletemergeconflictrow( @conflict_table sysname = '%', @source_object nvarchar(386) = NULL, @rowguid uniqueidentifier, @origin_datasource varchar(255), @drop_table_if_empty varchar(10) = 'false') as declare @retcode smallint declare @cmd nvarchar(4000) declare @rowguidstr nvarchar(40) declare @object sysname declare @owner sysname declare @tablenick int declare @tablenickstr nvarchar(11) set @rowguidstr = convert(nchar(36), @rowguid) /* Delete conflict from Conflict_<Table> */ if @conflict_table <> '%' begin select @cmd = 'delete from ' select @cmd = @cmd + quotename(@conflict_table) select @cmd = @cmd + ' where origin_datasource = ''' select @cmd = @cmd + @origin_datasource select @cmd = @cmd + ''' and rowguidcol = ''' select @cmd = @cmd + @rowguidstr select @cmd = @cmd + '''' exec (@cmd) if @@ERROR<>0 return (1) if LOWER(@drop_table_if_empty collate SQL_Latin1_General_CP1_CS_AS) = 'true' begin select @cmd = 'if not exists (select 1 from ' select @cmd = @cmd + quotename(@conflict_table) select @cmd = @cmd + ')' select @cmd = @cmd + ' update sysmergearticles set ins_conflict_proc = NULL, conflict_table = NULL where conflict_table = ' + quotename(@conflict_table) exec (@cmd) if @@ERROR<>0 return (1) select @cmd = 'if not exists (select 1 from ' select @cmd = @cmd + quotename(@conflict_table) select @cmd = @cmd + ')' select @cmd = @cmd + ' drop table ' select @cmd = @cmd + quotename(@conflict_table) select @cmd = @cmd + '' exec (@cmd) if @@ERROR<>0 return (1) end end /* Delete conflict from MSmerge_delete_conflicts */ else begin if @source_object is NULL begin raiserror(14043, 16, -1, '@source_object') return (1) end select @object = PARSENAME(@source_object, 1) select @owner = PARSENAME(@source_object, 2) execute @retcode=sp_MStablenickname @owner, @object, @tablenick output if @tablenick IS NULL or @@ERROR<>0 or @retcode<>0 BEGIN raiserror (20003, 11, -1, @object) RETURN (1) END set @tablenickstr = convert(nchar, @tablenick) select @cmd = 'delete from MSmerge_delete_conflicts' select @cmd = @cmd + ' where origin_datasource = ''' select @cmd = @cmd + @origin_datasource select @cmd = @cmd + ''' and tablenick = ' select @cmd = @cmd + @tablenickstr select @cmd = @cmd + ' and rowguid = ''' select @cmd = @cmd + @rowguidstr select @cmd = @cmd + '''' exec (@cmd) if @@ERROR<>0 return (1) end go exec dbo.sp_MS_marksystemobject sp_deletemergeconflictrow go /* ** Name : sp_getmergedeletetype ** Description: This sp returns the type of delete ** Parameters: ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. source_object (nvarchar(386)) Indicates the source object ** b. rowguid (uniqueidentifier) Row identifier for the delete conflict ** c. delete_type(OUTPUT) (int) Code indicating delete type ** User Delete - 1 ** Partial Delete - 5 ** System Delete - 6 */ raiserror('Creating procedure sp_getmergedeletetype', 0,1) GO CREATE PROCEDURE sp_getmergedeletetype( @source_object nvarchar (386), @rowguid uniqueidentifier, @delete_type int OUTPUT ) as declare @object sysname declare @owner sysname declare @tablenick int select @object = PARSENAME(@source_object, 1) select @owner = PARSENAME(@source_object, 2) execute dbo.sp_MStablenickname @owner, @object, @tablenick output if @tablenick IS NULL OR @@ERROR<>0 BEGIN raiserror (20003, 11, -1, @object) RETURN (1) END if @rowguid IS NULL begin raiserror (14027, 11, -1, '@rowguid') return (1) end select @delete_type = type from MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick go exec dbo.sp_MS_marksystemobject sp_getmergedeletetype go /* ** Name : sp_mergedummyupdate ** Description: This sp does a dummy updates on the given row so that it will be ** resent during the next merge. * Parameters: ** ---------------------------------------------------------------------------------- ** Name Datatype Description ** ---------------------------------------------------------------------------------- ** a. source_object (nvarchar(386)) Indicates the source object ** b. rowguid (uniqueidentifier) Row identifier for the delete conflict */ raiserror('Creating procedure sp_mergedummyupdate', 0,1) GO CREATE PROCEDURE sp_mergedummyupdate( @source_object nvarchar (386), @rowguid uniqueidentifier ) as declare @object sysname declare @owner sysname declare @tablenick int declare @tablenickstr nvarchar(11) select @object = PARSENAME(@source_object, 1) select @owner = PARSENAME(@source_object, 2) execute dbo.sp_MStablenickname @owner, @object, @tablenick output if @tablenick IS NULL or @@ERROR<>0 BEGIN raiserror (20003, 11, -1, @object) RETURN (1) END set @tablenickstr = convert(nchar, @tablenick) declare @rowguidstr nvarchar(40) if @rowguid IS NULL begin raiserror (14043, 11, -1, '@rowguid') return (1) end set @rowguidstr = convert(nchar(36), @rowguid) /* ** If the row does not exist in the base table, call sp_MSdummyupdate with metadata_type = 1 (tombstone) */ exec ('if not exists (select 1 from ' + @source_object + ' where rowguidcol = ''' + @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 1') /* ** If the row exists in the base table and MSmerge_contents , call sp_MSdummyupdate with metadata_type = 2 (contents) */ exec ('if exists (select 1 from ' + @source_object + ' where rowguidcol = ''' + @rowguidstr + ''' ) and exists (select rowguid from MSmerge_contents where rowguid = ''' + @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 2') /* ** If the row exists in the base table, but not in MSmerge_contents , call sp_MSdummyupdate with metadata_type = 3 (contents_deferred) */ exec ('if exists (select 1 from ' + @source_object + ' where rowguidcol = ''' + @rowguidstr + ''' ) and not exists (select rowguid from MSmerge_contents where rowguid = ''' + @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 3') go exec dbo.sp_MS_marksystemobject sp_mergedummyupdate go raiserror('Creating procedure sp_addtabletocontents', 0,1) GO create procedure sp_addtabletocontents (@table_name sysname, @owner_name sysname = NULL) AS declare @qualified_table_name nvarchar(270) declare @tablenick int declare @tablenickstr nvarchar(12) declare @repl_nick int declare @lineage varbinary(249) declare @colv varbinary(2000) declare @coltrack int declare @objid int declare @maxcolid int declare @retcode int declare @gen int set nocount on create table #temp_cont(rowguid uniqueidentifier) execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end if @owner_name is NULL begin select @owner_name = user_name(uid) from sysobjects where name = @table_name end set @qualified_table_name = QUOTENAME(@owner_name) + '.' + QUOTENAME(@table_name) set @objid = object_id(@qualified_table_name) if @objid is NULL return (1) select @gen = max(gen_cur), @tablenick = max(nickname), @coltrack = max(column_tracking) from sysmergearticles where objid = @objid if @gen is null set @gen = 0 select @maxcolid = max(colid) from syscolumns where id = @objid if @coltrack = 1 set @colv = { fn INITCOLVS(@maxcolid, @repl_nick) } else set @colv = NULL set @lineage = { fn UPDATELINEAGE(0x0, @repl_nick, 1) } set @tablenickstr = convert(nchar, @tablenick) exec ('insert into #temp_cont(rowguid) select RowGuidCol from ' + @qualified_table_name + ' where RowGuidCol not in (select rowguid from MSmerge_contents where tablenick = ' + @tablenickstr + ')') insert into MSmerge_contents (tablenick, rowguid, generation, joinchangegen, lineage, colv1) select @tablenick, rowguid, @gen, @gen, @lineage, @colv from #temp_cont drop table #temp_cont GO exec dbo.sp_MS_marksystemobject sp_addtabletocontents go grant exec on dbo.sp_addtabletocontents to public go raiserror('Creating procedure sp_MSaddpubtocontents', 0,1) GO create procedure sp_MSaddpubtocontents (@publication sysname) AS declare @pubid uniqueidentifier declare @tablenick int declare @retcode int declare @objid int declare @owner sysname declare @table sysname set nocount on select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid while @tablenick is not null begin select @objid = objid from sysmergearticles where pubid = @pubid and nickname = @tablenick select @owner = user_name(uid) from sysobjects where id = @objid set @table = OBJECT_NAME(@objid) exec @retcode = dbo.sp_addtabletocontents @table, @owner IF @@ERROR <> 0 or @retcode <> 0 return (1) select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid and nickname > @tablenick end GO exec dbo.sp_MS_marksystemobject sp_MSaddpubtocontents go grant exec on dbo.sp_MSaddpubtocontents to public go raiserror(15339,-1,-1,'sp_MSget_subtypedatasrc') GO CREATE PROCEDURE sp_MSget_subtypedatasrc @subscriber sysname, @distributor sysname, @distribdb sysname, @datasrctype int OUTPUT, @datasrc sysname OUTPUT AS SET NOCOUNT ON DECLARE @provider_name sysname DECLARE @jet_subscriber tinyint DECLARE @oledb_subscriber tinyint DECLARE @oracle_subscriber tinyint DECLARE @db2universal_subscriber tinyint DECLARE @distproc nvarchar(300) DECLARE @cmd nvarchar(512) set @jet_subscriber = 2 set @oledb_subscriber = 3 set @oracle_subscriber = 5 set @db2universal_subscriber = 6 create table #subtype ( type tinyint not null, ) create table #server_info ( providername nvarchar(256) collate database_default null, datasource nvarchar(3750) collate database_default null, -- this column is nvarchar(4000) in sysservers, but we want to avoid info msg -- from create table when row size > 8060 bytes. ) SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_executesql' SELECT @cmd = 'select type from MSsubscriber_info where UPPER(subscriber) = ''' + UPPER(@subscriber) + ''' and UPPER(publisher) = ''' + @@servername + '''' insert into #subtype exec @distproc @cmd if (@@error <> 0 or @@rowcount <> 1) begin drop table #subtype drop table #server_info raiserror(14085, 16, -1) return 1 end select @datasrctype = type from #subtype SELECT @cmd = 'select providername, datasource from master..sysservers where UPPER(srvname) = ''' + UPPER(@subscriber) + '''' insert into #server_info exec @distproc @cmd if (@@error <> 0 or @@rowcount <> 1) begin drop table #subtype drop table #server_info raiserror(14085, 16, -1) return 1 end select @datasrc = datasource, @provider_name = providername from #server_info /* ** Jet and Oracle subscribers are actually added to MSsubscriber_info as OLE DB subscribers, ** since they can be used in transactional replication also. ** Map the type to Jet or Oracle based on OLE DB provider name. */ if (@datasrctype = @oledb_subscriber) BEGIN if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MICROSOFT.JET.OLEDB.4.0') select @datasrctype = @jet_subscriber else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MSDAORA') select @datasrctype = @oracle_subscriber else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'DB2OLEDB') select @datasrctype = @db2universal_subscriber END drop table #subtype drop table #server_info return 0 GO exec dbo.sp_MS_marksystemobject sp_MSget_subtypedatasrc go grant exec on dbo.sp_MSget_subtypedatasrc to public go raiserror('Creating procedure sp_addmergealternatepublisher', 0,1) GO CREATE PROCEDURE sp_addmergealternatepublisher ( @publisher sysname, /* Publisher server */ @publisher_db sysname, /* Publisher database */ @publication sysname, /* Publication name */ @alternate_publisher sysname, /* Alternate publisher */ @alternate_publisher_db sysname, /* Alternate publisher_db */ @alternate_publication sysname, /* Alternate publication */ @alternate_distributor sysname, /* Alternate publisher's Distributor */ @friendly_name nvarchar(255) = NULL, /* Friendly Name for the association */ @reserved nvarchar(20) = NULL /* Reserved param for internal use only */ ) AS SET NOCOUNT ON /* ** Declarations. */ declare @retcode int declare @pubid uniqueidentifier declare @subid uniqueidentifier declare @alternate_subid uniqueidentifier declare @description nvarchar(255) declare @allow_synctoalternate bit declare @at_publisher bit declare @subscriber_type smallint set @at_publisher = 1 /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* ** Select the subscription's pubid and subid */ select @pubid = pubid, @allow_synctoalternate = allow_synctoalternate from sysmergepublications where name = @publication and publisher_db = @publisher_db and LOWER(publisher) = LOWER(@publisher) if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end /* If this is an external call, make the AtPublisher and allows syncto alternate check */ EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @@ERROR <> 0 or @retcode <> 0 set @at_publisher = 0 IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) <> 'internal' begin IF @at_publisher = 0 BEGIN RAISERROR (21215, 16, -1) RETURN (1) END if @allow_synctoalternate = 0 begin raiserror (21502, 11, -1, @publication) return (1) end end select @subid = subid from sysmergesubscriptions where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and pubid = @pubid /* ** Select the alternate publisher's subid */ select @alternate_subid = subid, @subscriber_type = subscriber_type from sysmergesubscriptions where publication = @alternate_publication and LOWER(subscriber_server) = LOWER(@alternate_publisher) and db_name = @alternate_publisher_db IF @subid is NULL or @alternate_subid IS NULL BEGIN RAISERROR(21216, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) RETURN (1) END IF @subscriber_type <> 1 BEGIN RAISERROR(21216, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) RETURN (1) END IF @friendly_name IS NULL begin select @description = description from sysmergesubscriptions where subid = @alternate_subid if @description IS NULL begin select @description = formatmessage(21217, @alternate_publication, @alternate_publisher) end end else select @description = @friendly_name if not exists (select * from MSmerge_altsyncpartners where subid = @subid and alternate_subid = @alternate_subid) begin insert into MSmerge_altsyncpartners(subid, alternate_subid, description) values(@subid, @alternate_subid, @description) if @@ERROR <> 0 BEGIN GOTO FAILURE END update sysmergesubscriptions set distributor = @alternate_distributor where subid = @alternate_subid if @@ERROR <> 0 or @@ROWCOUNT <> 1 BEGIN GOTO FAILURE END end if not exists (select * from MSmerge_altsyncpartners where subid = @alternate_subid and alternate_subid = @subid) begin insert into MSmerge_altsyncpartners(subid, alternate_subid, description) values(@alternate_subid, @subid, @description) if @@ERROR <> 0 BEGIN GOTO FAILURE END end return (0) FAILURE: RAISERROR(21243, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) RETURN (1) go exec dbo.sp_MS_marksystemobject sp_addmergealternatepublisher go grant exec on dbo.sp_addmergealternatepublisher to public go raiserror('Creating procedure sp_helpmergealternatepublisher', 0,1) GO CREATE PROCEDURE sp_helpmergealternatepublisher ( @publisher sysname, /* Publisher server */ @publisher_db sysname, /* Publisher database */ @publication sysname /* Publication name */ ) AS SET NOCOUNT ON declare @subid uniqueidentifier declare @pubid uniqueidentifier declare @distributor sysname declare @retcode int declare @MShelpmergealternatepublisher TABLE ( subid uniqueidentifier not null, alternate_publisher sysname collate database_default not null, alternate_publisher_db sysname collate database_default not null, alternate_publication sysname collate database_default not null, alternate_distributor sysname collate database_default null, friendly_name nvarchar(255) collate database_default null, enabled bit not null ) /* ** Select the subscription's pubid and subid */ select @pubid = pubid from sysmergepublications where name = @publication and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db if @pubid is NULL begin raiserror (20026, 11, -1, @publication) return (1) end select @subid = subid, @distributor = distributor from sysmergesubscriptions where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and pubid = @pubid IF @@ERROR <> 0 BEGIN GOTO FAILURE END insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled) select distinct subs.subid, subs.subscriber_server, subs.db_name, subs.publication, subs.distributor, subs.description, 1 from sysmergesubscriptions subs where subs.subid in (select alternate_subid from MSmerge_altsyncpartners where subid = @subid) /* select the parent publisher */ insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled) select distinct pubs.pubid, pubs.publisher, pubs.publisher_db, pubs.name, @distributor, pubs.description, 1 from sysmergepublications pubs where pubs.name = @publication and LOWER(publisher) = LOWER(@publisher) and publisher_db = @publisher_db insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled) select distinct subs.subid, subs.subscriber_server, subs.db_name, subs.publication, subs.distributor, subs.description, 0 from sysmergesubscriptions subs where subs.subscriber_type = 1 -- only publisher and republishers and subs.subid = subs.pubid -- only publisher and republishers and subs.subid not in (select subid from @MShelpmergealternatepublisher) select distinct alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled from @MShelpmergealternatepublisher order by alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor return (0) FAILURE: RETURN (1) go exec dbo.sp_MS_marksystemobject sp_helpmergealternatepublisher go grant exec on dbo.sp_helpmergealternatepublisher to public go raiserror('Creating procedure sp_dropmergealternatepublisher', 0,1) GO CREATE PROCEDURE sp_dropmergealternatepublisher ( @publisher sysname, /* Publisher server */ @publisher_db sysname, /* Publisher database */ @publication sysname, /* Publication name */ @alternate_publisher sysname, /* Alternate publisher */ @alternate_publisher_db sysname, /* Alternate publisher_db */ @alternate_publication sysname /* Alternate publication */ ) AS SET NOCOUNT ON declare @retcode int declare @subid uniqueidentifier declare @alternate_subid uniqueidentifier /* ** Security Check */ EXEC @retcode = dbo.sp_MSreplcheck_publish IF @@ERROR <> 0 or @retcode <> 0 return (1) /* Check is there is an invalid attempt to drop the default publiser/publisher_db/publication */ if (LOWER(@publisher) = LOWER(@alternate_publisher)) AND (@publisher_db = @alternate_publisher_db) AND (@publication = @alternate_publication) begin RAISERROR(21252, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) return (1) end select @subid = subid from sysmergesubscriptions where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and publication = @publication /* ** Select the alternate publisher's subid */ select @alternate_subid = subid from sysmergesubscriptions where publication = @alternate_publication and LOWER(subscriber_server) = LOWER(@alternate_publisher) and db_name = @alternate_publisher_db IF @subid is NULL or @alternate_subid IS NULL BEGIN RAISERROR(21216, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) RETURN (1) END if exists (select * from MSmerge_altsyncpartners where subid = @subid and alternate_subid = @alternate_subid) begin delete from MSmerge_altsyncpartners where @subid = subid and alternate_subid = @alternate_subid if @@ERROR <> 0 or @@rowcount <> 1 BEGIN GOTO FAILURE END end return (0) FAILURE: RAISERROR(21251, 11, -1, @alternate_publisher, @alternate_publisher_db, @alternate_publication) RETURN (1) go exec dbo.sp_MS_marksystemobject sp_dropmergealternatepublisher go grant exec on dbo.sp_dropmergealternatepublisher to public go raiserror(15339,-1,-1,'sp_MScomputemergearticlescreationorder') GO CREATE PROCEDURE sp_MScomputemergearticlescreationorder @publication sysname AS SET NOCOUNT ON DECLARE @pubid uniqueidentifier DECLARE @max_level int DECLARE @current_level int DECLARE @update_level int DECLARE @limit int SELECT @pubid = NULL -- Get the pubid from sysmergepublications SELECT @pubid = pubid FROM sysmergepublications WHERE name = @publication AND UPPER(publisher) = UPPER(@@SERVERNAME) AND publisher_db = DB_NAME() IF @@ERROR <> 0 RETURN (1) IF @pubid IS NULL BEGIN RAISERROR(20026, 16, -1, @publication) RETURN (1) END -- Find out the total number of articles in this publication and -- compute the maximum tree height based on the number of articles in -- the publication. Here, the tree height is counted from the -- leaf-nodes towards the root(s) starting from @max_level SELECT @max_level = COUNT(*) + 10, @limit = 2 * COUNT(*) + 11 FROM sysmergeextendedarticlesview WHERE pubid = @pubid IF @@ERROR <> 0 BEGIN RETURN (1) END -- The following temp table contains the minimal amount of -- article information that we want to keep around and the current -- computed tree level of the article CREATE TABLE #article_level_info ( article sysname collate database_default not null, source_objid INT NOT NULL, tree_level INT NOT NULL, nickname INT NOT NULL, major_type TINYINT NOT NULL -- 1-view&func, 0-other ) CREATE CLUSTERED INDEX ucarticle_level_info ON #article_level_info(source_objid) IF @@ERROR <> 0 BEGIN GOTO Failure END -- Populate the article level info table. All articles will be -- assigned 0 as their initial tree level. Having -- a tree level of 0 means that the algorithm hasn't discovered -- any objects that the article depends on within the publication. INSERT INTO #article_level_info SELECT name, objid, 0, ISNULL(nickname, 5*@max_level), CASE type WHEN 0x40 THEN 1 WHEN 0x80 THEN 1 ELSE 0 END FROM sysmergeextendedarticlesview WHERE pubid = @pubid -- To jump-start the algorithm, update the tree_level of -- all articles with no dependency to @max_level. UPDATE #article_level_info SET tree_level = @max_level WHERE NOT EXISTS (SELECT * FROM sysdepends WHERE source_objid = id AND id <> depid) IF @@ERROR <> 0 GOTO Failure -- For each increasing tree level starting from @max_level, update the -- the tree_level of articles depending on objects at the current -- level to current level + 1 SELECT @current_level = @max_level WHILE 1 = 1 BEGIN SELECT @update_level = @current_level + 1 UPDATE #article_level_info SET tree_level = @update_level FROM #article_level_info INNER JOIN sysdepends d ON #article_level_info.source_objid = d.id INNER JOIN #article_level_info ali1 ON (d.depid = ali1.source_objid AND ali1.tree_level = @current_level AND d.id <> d.depid) -- Terminate the algorithm if we cannot find any articles -- depending on articles at the current level IF @@ROWCOUNT = 0 GOTO PHASE1 IF @@ERROR <> 0 GOTO Failure SELECT @current_level = @current_level + 1 -- Although there should not be any circular -- dependencies among the articles, the following -- check is performed to guarantee that -- the algorithm will terminate even if there -- is circular dependency among the articles -- Note that with at least one node per level, -- the current level can never exceed the total -- number of articles (nodes) unless there is -- circular dependency among the articles. -- @limit is defined to be # of articles + 1 -- although @limit = # of articles - 1 will be -- sufficient. This is to make absolutely sure that -- the algorithm will never terminate too early IF @current_level > @limit GOTO PHASE1 END PHASE1: -- There may be interdependencies among articles -- that haven't been included in the previous calculations so -- we compute the proper order among these articles here. SELECT @limit = @max_level - 9 SELECT @current_level = 0 WHILE 1 = 1 BEGIN SELECT @update_level = @current_level + 1 UPDATE #article_level_info SET tree_level = @update_level FROM #article_level_info INNER JOIN sysdepends d ON (#article_level_info.source_objid = d.id AND #article_level_info.tree_level < @max_level) INNER JOIN #article_level_info ali1 ON (d.depid = ali1.source_objid AND ali1.tree_level = @current_level AND d.id <> d.depid) IF @@ROWCOUNT = 0 GOTO PHASE2 IF @@ERROR <> 0 GOTO Failure SELECT @current_level = @current_level + 1 IF @current_level > @limit GOTO PHASE2 END PHASE2: -- Select the articles out of #article_level_info -- in ascending order of tree_level. This will give -- the proper order in which articles can be created -- without violating the internal dependencies among -- the themselves. Note that this algorithm still allows -- unresolved external references outside the publication. -- All this algorithm can guarantee is that all articles will -- be created successfully using the resulting order if -- there is no dependent object outside the publication. SELECT article FROM #article_level_info ORDER BY major_type ASC, tree_level ASC, nickname ASC DROP TABLE #article_level_info RETURN (0) Failure: DROP TABLE #article_level_info RETURN (1) GO exec dbo.sp_MS_marksystemobject sp_MScomputemergearticlescreationorder go grant exec on dbo.sp_MScomputemergearticlescreationorder to public go raiserror(15339,-1,-1,'sp_MScomputemergeunresolvedrefs') GO CREATE PROCEDURE sp_MScomputemergeunresolvedrefs @publication sysname, -- Must provide the publication name @article sysname = '%' -- '%' means all articles in the specified publication, otherwise an exact match is performed AS SET NOCOUNT ON DECLARE @pubid uniqueidentifier -- Parameter check: @publication IF @publication IS NULL BEGIN RAISERROR (14043, 16, -1, '@publication') RETURN (1) END SELECT @pubid = NULL -- Get the pubid of the publication SELECT @pubid = pubid FROM sysmergepublications WHERE name = @publication AND UPPER(publisher) = UPPER(@@SERVERNAME) AND publisher_db = DB_NAME() IF @pubid IS NULL BEGIN RAISERROR (20026, 11, -1, @publication) RETURN (1) END SELECT DISTINCT 'article' = a.name, 'dependent object' = o.name, 'dependent object owner' = u.name, 'dependent objectid' = o.id FROM dbo.sysmergeextendedarticlesview a INNER JOIN sysdepends dep ON a.objid = dep.id AND a.pubid = @pubid AND (@article = '%' OR name = @article) AND dep.depid NOT IN (SELECT objid FROM dbo.sysmergeextendedarticlesview WHERE pubid = @pubid AND (@article = '%' OR name = @article)) INNER JOIN sysobjects o ON dep.depid = o.id INNER JOIN sysusers u ON u.uid = o.uid GO exec dbo.sp_MS_marksystemobject sp_MScomputemergeunresolvedrefs go grant exec on dbo.sp_MScomputemergeunresolvedrefs to public go raiserror('Creating procedure sp_MSCheckmergereplication', 0,1) GO CREATE PROCEDURE sp_MSCheckmergereplication AS SET NOCOUNT ON declare @category int declare @db_name sysname select @db_name=db_name() select @category=category FROM master..sysdatabases WHERE name = @db_name collate database_default if @category & 4 = 0 begin raiserror(21147, 16, -1, @db_name) return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSCheckmergereplication go grant execute on dbo.sp_MSCheckmergereplication to public go raiserror('Creating procedure sp_MSgetpubinfo', 0,1) GO CREATE PROCEDURE sp_MSgetpubinfo ( @publication sysname, /* The publication name */ @publisher sysname, @pubdb sysname ) AS SET NOCOUNT ON select pubid from sysmergepublications where name = @publication and LOWER(publisher) = LOWER(@publisher) and LOWER(publisher_db) = LOWER(@pubdb) go exec dbo.sp_MS_marksystemobject sp_MSgetpubinfo go grant execute on dbo.sp_MSgetpubinfo to public go raiserror('Creating procedure sp_MSaddmergedynamicsnapshotjob', 0,1) go -- -- Name: sp_MSaddmergedynamicsnapshotjob -- -- Description: This procedure sets up a SQL Server Agent job for dynamic -- snapshot generation and associates a row in -- MSdynamicsnapshotjobs for the job to the specified publication. -- -- Notes: 1) If a local path is specified for the @dynamic_snapshot_location, -- the local path of the Distribution server will be used. -- 2) This procedure will not check whether the given path is already in -- use by another dynamic snapshot generation job. Sharing the same -- dynamic snapshot location among different dynamic snapshot -- generation jobs can lead to file corruption and/or snapshot files -- being overwritten. -- 3) A regular snapshot job must be added for the publication before -- a dynamic snapshot generation job can be scheduled. -- 4) This procedure will not check for the existence of the given -- dynamic snapshot location. -- 5) The specified publication must be enabled for dynamic filtering. -- 6) If @dynamic_snapshot_jobname is specified, it must be unique -- among all the jobs at the distributor's msdb. If it is left -- unspecified, a job name will be generated according to the -- following rule: -- 'dyn_' + (job name for the regular snapshot job) + (guid string) -- Note that (job name for the regular snapshot job) can be truncated -- if the resulting name is too long. -- -- Parameters: @publication sysname (mandatory) -- @dynamic_filter_login sysname (optional, default null) -- @dynamic_filter_hostname sysname (optional, default null) -- @dynamic_snapshot_location nvarchar(255) (mandatory) -- @dynamic_snapshot_jobid (optional, output, default null) -- @dynamic_snapshot_jobname (optional, output, default null) -- Scheduling information: -- @frequency_type int (optional, default 4 == Daily) -- @frequency_interval int (optional, default 1 == Every day) -- @frequency_subday int (optional, default 4 (Sub interval = Minute)) -- @frequency_subday_interval int (optional, default 5 == Every five minutes) -- @frequency_relative_interval int (optional, default 1) -- @frequency_recurrence_factor int (optional, default 0) -- @active_start_date int (optional, default 0 == Today) -- @active_end_date int (optional, default 99991231) -- @active_start_time_of_day int (optional, default 0 == Now) -- @active_end_time_of_day int (optional, default 235959) -- -- Returns: 0 - succeeded -- 1 - failed -- -- Result set (upon successful completion of the operation): -- dynamic_snapshot_jobname sysname -- dynamic_snapshot_jobid uniqueidentifier -- Security: Only members of the 'sysadmin' server role and members of the -- 'db_owner' database role can invoke this procedure successfully. -- Security check is performed inside the procedure. -- create procedure sp_MSaddmergedynamicsnapshotjob ( @publication sysname, @dynamic_filter_login sysname = null, @dynamic_filter_hostname sysname = null, @dynamic_snapshot_location nvarchar(255), @dynamic_snapshot_jobname sysname = null output, @dynamic_snapshot_jobid uniqueidentifier = null output, -- Scheduling information @frequency_type int = 4, @frequency_interval int = 1, @frequency_subday int = 4, @frequency_subday_interval int = 5, @frequency_relative_interval int = 1, @frequency_recurrence_factor int = 0, @active_start_date int = 0, @active_end_date int = 99991231, @active_start_time_of_day int = 0, @active_end_time_of_day int = 235959 ) as begin set nocount on declare @retcode int declare @pubid uniqueidentifier declare @dynamic_filters bit declare @snapshot_jobid uniqueidentifier declare @command_line nvarchar(4000) declare @distribdb sysname declare @rpcsrvname sysname declare @distributor sysname declare @fjobcreated bit declare @distproc nvarchar(4000) declare @id int -- Initializations select @retcode = 0 select @pubid = null select @dynamic_filters = 0 select @snapshot_jobid = null select @fjobcreated = 0 if not exists (select * from sysobjects where name = 'sysmergepublications') begin raiserror (20054, 16, -1) return (1) end -- Make sure that caller is a member of 'sysadmins' or 'db_owner' exec @retcode = dbo.sp_MSreplcheck_publish if @@error <> 0 or @retcode <> 0 return (1) -- Check that the given dynammic filter login is in the PAL exec @retcode = sp_check_publication_access @publication = @publication, @given_login = @dynamic_filter_login if @retcode <> 0 or @@error <> 0 return 1 select @dynamic_snapshot_location = ltrim(@dynamic_snapshot_location) -- Specified @dynamic_snapshot_location must be non-empty if @dynamic_snapshot_location is null or @dynamic_snapshot_location = N'' begin raiserror(21321, 16, -1) return (1) end -- Verify that the given publication exists and get the pubid at the -- same time select @pubid = pubid, @dynamic_filters = dynamic_filters from dbo.sysmergepublications where upper(publisher) = upper(@@servername) and publisher_db = db_name() and name = @publication if @pubid is null begin raiserror(20026, 16, -1, @publication) return (1) end -- The given publication must be enabled for dynamic filtering if @dynamic_filters <> 1 begin raiserror(21323, 16, -1) return (1) end -- A regular snapshot job is required before a dynamic snapshot job -- can be scheduled select @snapshot_jobid = snapshot_jobid from MSmerge_replinfo where repid = @pubid if @snapshot_jobid is null begin raiserror(21324, 16, -1) return (1) end -- The given dynamic snapshot job name cannot be '%' and it cannot match -- any of the existing dynamic snapshot job name if @dynamic_snapshot_jobname = '%' begin raiserror(21327, 16, -1) return (1) end if exists (select * from MSdynamicsnapshotjobs where name = @dynamic_snapshot_jobname) begin raiserror(21328, 16, -1, @dynamic_snapshot_jobname) return (1) end -- Get distributor information for RPC exec @retcode = sp_helpdistributor @distributor = @distributor output, @distribdb = @distribdb output, @rpcsrvname = @rpcsrvname output if @@error <> 0 or @retcode <> 0 return (1) select @distproc = rtrim(@rpcsrvname) + N'.' + @distribdb + N'.' + N'dbo.sp_MSadddynamicsnapshotjobatdistributor' exec @retcode = @distproc @regular_snapshot_jobid = @snapshot_jobid, @dynamic_filter_login = @dynamic_filter_login, @dynamic_filter_hostname = @dynamic_filter_hostname, @dynamic_snapshot_location = @dynamic_snapshot_location, @dynamic_snapshot_jobname = @dynamic_snapshot_jobname output, @dynamic_snapshot_jobid = @dynamic_snapshot_jobid output, @freqtype = @frequency_type, @freqinterval = @frequency_interval, @freqsubtype = @frequency_subday, @freqsubinterval = @frequency_subday_interval, @freqrelativeinterval = @frequency_relative_interval, @freqrecurrencefactor = @frequency_recurrence_factor, @activestartdate = @active_start_date, @activeenddate = @active_end_date, @activestarttimeofday = @active_start_time_of_day, @activeendtimeofday = @active_end_time_of_day if @retcode <> 0 or @@error <> 0 goto Failure select @fjobcreated = 1 insert MSdynamicsnapshotjobs (name, pubid, job_id, dynamic_filter_login, dynamic_filter_hostname, dynamic_snapshot_location) values (@dynamic_snapshot_jobname, @pubid, @dynamic_snapshot_jobid, @dynamic_filter_login, @dynamic_filter_hostname, @dynamic_snapshot_location) if @@error <> 0 begin goto Failure end select @id = @@identity select 'id' = @id, 'dynamic_snapshot_jobname' = @dynamic_snapshot_jobname, 'dynamic_snapshot_jobid' = @dynamic_snapshot_jobid return 0 Failure: if @fjobcreated = 1 begin select @distproc = rtrim(@rpcsrvname) + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeleterepljob' exec @distproc @job_id = @dynamic_snapshot_jobid end return @retcode end go exec dbo.sp_MS_marksystemobject sp_MSaddmergedynamicsnapshotjob go grant execute on dbo.sp_MSaddmergedynamicsnapshotjob to public go raiserror('Creating procedure sp_MSdropmergedynamicsnapshotjob', 0,1) go -- -- Name: sp_MSdropmergedynamicsnapshotjob -- -- Description: This procedure drops a scheduled dynamic snapshot job for -- a publication and the associated meta-data in -- MSdynamicsnapshotjobs. This procedure will also remove all -- files in the associated dynamic snapshot location. -- -- Parameters: @publication sysname (mandatory) -- @dynamic_snapshot_jobname (optional, default '%') -- @dynamic_snapshot_jobid uniqueidentifier (optional, default -- null) When @dynamic_snapshot_jobid is null and -- @dynamic_snapshot_jobname is '%', all dynamic snapshot -- jobs for the specified publication will be dropped. -- @ignore_distributor bit (optional, default 0) -- -- Notes: 1) At most one of @dynamic_snapshot_jobid and -- @dynamic_snapshot_jobname can be specified with a non-default -- value. -- -- Returns: 0 - succeeded -- 1 - failed -- -- Security: Only members of the 'sysadmin' server role and the 'db_owner' -- database role can execute this procedure successfully even though execute -- permission of this procedure is granted to public. -- create procedure sp_MSdropmergedynamicsnapshotjob ( @publication sysname, @dynamic_snapshot_jobname sysname = '%', @dynamic_snapshot_jobid uniqueidentifier = null, @ignore_distributor bit = 0 ) as begin set nocount on declare @retcode int declare @pubid uniqueidentifier declare @dynamic_snapshot_location nvarchar(255) declare @guidstr nvarchar(40) declare @dynamic_snapshot_jobid_from_cursor uniqueidentifier declare @distributor sysname declare @distribdb sysname declare @rpcsrvname sysname declare @distproc nvarchar(4000) select @retcode = 0 select @pubid = null select @dynamic_snapshot_location = null if not exists (select * from sysobjects where name = 'sysmergepublications') begin raiserror (20054, 16, -1) return (1) end -- Make sure that caller is a member of 'sysadmins' or 'db_owner' exec @retcode = dbo.sp_MSreplcheck_publish if @@error <> 0 or @retcode <> 0 return (1) select @pubid = pubid from dbo.sysmergepublications where name = @publication and upper(publisher) = upper(@@servername) and publisher_db = db_name() if @pubid is null begin raiserror(20026, 16, -1, @publication) return (1) end -- At most one of @dynamic_snapshot_jobid and @dynamic_snapshot_jobname -- can be specified with a non-default value if @dynamic_snapshot_jobid is not null and @dynamic_snapshot_jobname <> N'%' begin raiserror(21329, 16, -1) return (1) end if @dynamic_snapshot_jobid is null and @dynamic_snapshot_jobname = N'%' begin declare hJobsCursor cursor local fast_forward for select job_id from MSdynamicsnapshotjobs where pubid = @pubid if @@error <> 0 return 1 open hJobsCursor if @@error <> 0 return 1 fetch hJobsCursor into @dynamic_snapshot_jobid_from_cursor begin transaction save transaction sp_MSdropdynamicsnapshotjobC while (@@fetch_status <> -1) begin if @dynamic_snapshot_jobid_from_cursor is not null begin exec @retcode = sp_MSdropmergedynamicsnapshotjob @publication = @publication, @dynamic_snapshot_jobid = @dynamic_snapshot_jobid_from_cursor, @ignore_distributor = @ignore_distributor if @@error <> 0 or @retcode <> 0 goto CursorFailure end fetch hJobsCursor into @dynamic_snapshot_jobid_from_cursor end commit transaction close hJobsCursor deallocate hJobsCursor return 0 CursorFailure: rollback transaction sp_MSdropdynamicsnapshotjobC commit transaction close hJobsCursor deallocate hJobsCursor return 1 end select @pubid = pubid from dbo.sysmergepublications where name = @publication and upper(publisher) = upper(@@servername) and publisher_db = db_name() if @dynamic_snapshot_jobid is null begin select @dynamic_snapshot_location = dynamic_snapshot_location, @dynamic_snapshot_jobid = job_id from dbo.MSdynamicsnapshotjobs where pubid = @pubid and name = @dynamic_snapshot_jobname end else begin select @dynamic_snapshot_location = dynamic_snapshot_location from dbo.MSdynamicsnapshotjobs where pubid = @pubid and job_id = @dynamic_snapshot_jobid end if @dynamic_snapshot_location is null begin if @dynamic_snapshot_jobid is null begin raiserror(21326, 16, -1, N'@dynamic_snapshot_jobname', @dynamic_snapshot_jobname) end else begin select @guidstr = coalesce(convert(nvarchar(40), @dynamic_snapshot_jobid) collate database_default, '(NULL)' collate database_default) raiserror(21326, 16, -1, N'@dynamic_snapshot_jobid', @guidstr) end return (1) end -- Get distributor info for RPC if @ignore_distributor = 0 begin exec @retcode = sp_helpdistributor @distributor = @distributor output, @distribdb = @distribdb output, @rpcsrvname = @rpcsrvname output select @rpcsrvname = rtrim(@rpcsrvname) select @distproc = @rpcsrvname + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeletefoldercontents' -- Try to delete the files exec @retcode = @distproc @dynamic_snapshot_location -- Ignore errors end select @retcode = 0 begin transaction save transaction sp_MSdropmergedynamicsnapshotjob -- Delete row from MSdynamicsnapshotjobs delete dbo.MSdynamicsnapshotjobs where pubid = @pubid and job_id = @dynamic_snapshot_jobid if @@error <> 0 goto Failure if @ignore_distributor = 0 begin -- Drop the dynamic snapshot job at the distributor select @distproc = @rpcsrvname + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeleterepljob' exec @retcode = @distproc @dynamic_snapshot_jobid if @retcode <> 0 or @@error <> 0 goto Failure end commit transaction return 0 Failure: rollback transaction sp_MSdropmergedynamicsnapshotjob commit transaction return 1 end go exec dbo.sp_MS_marksystemobject sp_MSdropmergedynamicsnapshotjob grant execute on sp_MSdropmergedynamicsnapshotjob to public go raiserror('Creating procedure sp_MShelpmergedynamicsnapshotjob', 0, 1) go -- -- Name: sp_MShelpmergedynamicsnapshotjob -- -- Description: This procedure returns a listing of dynamic snapshot jobs. -- -- Parameters: @publication sysname (optional, default '%'): When @publication -- is '%', all dynamic snapshot jobs with the matching -- @dynamic_snapshot_jobid and @dynamic_snapshot_jobname will be -- returned. -- @dynamic_snapshot_jobname sysname (optional, default '%'): When -- @dynamic_snapshot_jobname is '%', all dynamic snapshot jobs that -- belong to @publication with the matching @dynamic_snapshot_jobid -- will be returned. -- @dynamic_snapshot_jobid (optional, default null): When -- @dynamic_snapshot_jobid is null, all dynamic snapshot jobs -- that belong to @publication with the matching -- @dynamic_snapshot_jobname will be returned. -- -- Notes: If all parameters are left unspecified when this procedure is called, -- all dynamic snapshot jobs for the current database will be returned. -- -- Result: id int -- job_name sysname -- job_id uniqueidentifier -- job id of the dynamic snapshot job -- dynamic_filter_login sysname -- dynamic_filter_hostname sysname -- dynamic_snapshot_location nvarchar(255) -- Returns: 0 - succeeded -- 1 - failed -- -- Security: Execute permission of this stored procedure is granted to public -- create procedure sp_MShelpmergedynamicsnapshotjob ( @publication sysname = N'%', @dynamic_snapshot_jobname sysname = N'%', @dynamic_snapshot_jobid uniqueidentifier = null ) as begin set nocount on if not exists (select * from sysobjects where name = 'sysmergepublications') begin return 0 end select 'id' = j.id, 'job_name' = j.name, 'job_id' = j.job_id, 'dynamic_filter_login' = j.dynamic_filter_login, 'dynamic_filter_hostname' = j.dynamic_filter_hostname, 'dynamic_snapshot_location' = j.dynamic_snapshot_location from sysmergepublications p inner join MSdynamicsnapshotjobs j on p.pubid = j.pubid where (p.name = @publication or @publication = N'%') and (j.name = @dynamic_snapshot_jobname or @dynamic_snapshot_jobname = N'%') and (j.job_id = @dynamic_snapshot_jobid or @dynamic_snapshot_jobid is null) if @@error <> 0 return (1) else return (0) end go exec dbo.sp_MS_marksystemobject sp_MShelpmergedynamicsnapshotjob go grant execute on dbo.sp_MShelpmergedynamicsnapshotjob to public go create procedure sp_MSremove_userscript( @pubid uniqueidentifier, @drop_publication bit = 0 )as declare @retention int declare @last_snapshot datetime declare @post_snapshot_ver int declare @post_snapshot_type int declare @user_script_type int declare @retcode int declare @len int declare @file_path nvarchar(4000) declare @delfile_cmd nvarchar(4000) declare @rmdir_cmd nvarchar(4000) select @post_snapshot_type=52 select @user_script_type=46 if not exists (select * from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type) return (0) select @retention=retention from sysmergepublications where pubid=@pubid select @last_snapshot=last_validated from sysmergesubscriptions where pubid=@pubid and subid=@pubid --I do not want to remove script files by setting retention to 0 if (@retention=0 or dateadd(day, -@retention, getdate()) < @last_snapshot) and @drop_publication = 0 return (0) select @post_snapshot_ver=schemaversion from sysmergeschemachange where schematype=@post_snapshot_type and pubid=@pubid --only get those script that can be safely removed declare #per_script cursor local fast_forward for select schematext from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type and (schemaversion<@post_snapshot_ver or @drop_publication = 1) open #per_script fetch #per_script into @file_path while (@@fetch_status<>-1) begin if(left(@file_path, 1) = N'0' or left(@file_path, 1) = N'1') select @file_path = right(@file_path, len(@file_path) - 1) select @delfile_cmd = N'del "' + @file_path + N'"' EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT if @@ERROR<>0 goto FAILURE select @len=CHARINDEX ( '\' , reverse(@file_path) ) select @file_path=SUBSTRING(@file_path , 1 , len(@file_path)-@len + 1) select @delfile_cmd = N'rmdir "' + @file_path + N'"' EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT if @@ERROR<>0 goto FAILURE fetch next from #per_script into @file_path end close #per_script deallocate #per_script return (0) FAILURE: close #per_script deallocate #per_script return (1) go EXEC dbo.sp_MS_marksystemobject 'sp_MSremove_userscript' go grant execute on sp_MSremove_userscript to public go dump tran master with no_log go checkpoint go use master go execute dbo.sp_configure 'update',1 go reconfigure with override go set ANSI_NULLS off go dump tran master with no_log go /* ** Drop the stored procedures in this script using the old dropping SP ** and then drop itself */ if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdrop_rlrecon') begin drop procedure sp_MSdrop_rlrecon end /* ** Create stored procedures to drop the stored procedures ** created by this script */ raiserror('Creating procedure sp_MSdrop_rlrecon', 0,1) GO create procedure sp_MSdrop_rlrecon as if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddinitialpublication') drop procedure sp_MSaddinitialpublication if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddinitialsubscription') drop procedure sp_MSaddinitialsubscription if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdropconstraints') drop procedure sp_MSdropconstraints if exists (select * from sysobjects where type = 'P' and name = 'sp_MSexclause') drop procedure sp_MSexclause if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetcolordinalfromcolname') drop procedure sp_MSgetcolordinalfromcolname if exists (select * from sysobjects where type = 'P' and name = 'sp_MSinsertbeforeimageclause') drop procedure sp_MSinsertbeforeimageclause if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakectsview') drop procedure sp_MSmakectsview if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeinsertproc') drop procedure sp_MSmakeinsertproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeupdateproc') drop procedure sp_MSmakeupdateproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeselectproc') drop procedure sp_MSmakeselectproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSreplcheck_permission') drop procedure sp_MSreplcheck_permission if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSinsertschemachange') drop procedure sp_MSinsertschemachange if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddinitialarticle') drop procedure sp_MSaddinitialarticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSaddinitialschemaarticle') drop procedure sp_MSaddinitialschemaarticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSinitdynamicsubscriber') drop procedure sp_MSinitdynamicsubscriber if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakearticleprocs') drop procedure sp_MSmakearticleprocs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSchecksnapshotstatus') drop procedure sp_MSchecksnapshotstatus if exists (select * from sysobjects where type = 'P' and name = 'sp_MSupdatesysmergearticles') drop procedure sp_MSupdatesysmergearticles if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdroparticletombstones') drop procedure sp_MSdroparticletombstones if exists (select * from sysobjects where type = 'P' and name = 'sp_MSproxiedmetadata') drop procedure sp_MSproxiedmetadata if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergearticles') drop procedure sp_MShelpmergearticles if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergeidentity') drop procedure sp_MShelpmergeidentity if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpmergeschemaarticles') drop procedure sp_MShelpmergeschemaarticles if exists (select * from sysobjects where type = 'P' and name = 'sp_MScheckidentityrange') drop procedure sp_MScheckidentityrange if exists (select * from sysobjects where type = 'P' and name = 'sp_MSfetchidentityrange') drop procedure sp_MSfetchidentityrange if exists (select * from sysobjects where type = 'P' and name = 'sp_MScreateretry') drop procedure sp_MScreateretry if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdropretry') drop procedure sp_MSdropretry if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdroptemptable') drop procedure sp_MSdroptemptable if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumretries') drop procedure sp_MSenumretries if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdeleteretry') drop procedure sp_MSdeleteretry if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetonerow') drop procedure sp_MSgetonerow if exists (select * from sysobjects where type = 'P' and name = 'sp_MSchangearticleresolver') drop procedure sp_MSchangearticleresolver if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetlastrecgen') drop procedure sp_MSgetlastrecgen if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetlastsentgen') drop procedure sp_MSgetlastsentgen if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetlastsentrecgens') drop procedure sp_MSgetlastsentrecgens if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetlastrecgen') drop procedure sp_MSsetlastrecgen if exists (select * from sysobjects where type = 'P' and name = 'sp_MSbelongs') drop procedure sp_MSbelongs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetupbelongs') drop procedure sp_MSsetupbelongs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetupworktables') drop procedure sp_MSsetupworktables if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetupnotbelongs') drop procedure sp_MSsetupnotbelongs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetupbelongs_withoutviewproc') drop procedure sp_MSsetupbelongs_withoutviewproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSexpandbelongs') drop procedure sp_MSexpandbelongs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSexpandnotbelongs') drop procedure sp_MSexpandnotbelongs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumpartialdeletes') drop procedure sp_MSenumpartialdeletes if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetlastsentgen') drop procedure sp_MSsetlastsentgen if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdummyupdate') drop procedure sp_MSdummyupdate if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdeletepushagent') drop procedure sp_MSdeletepushagent if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumgenerations') drop procedure sp_MSenumgenerations if exists (select * from sysobjects where type = 'P' and name = 'sp_MScheckexistsgeneration') drop procedure sp_MScheckexistsgeneration if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumreplicas') drop procedure sp_MSenumreplicas if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumdeletesmetadata') drop procedure sp_MSenumdeletesmetadata if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumchanges') drop procedure sp_MSenumchanges if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumpartialchanges') drop procedure sp_MSenumpartialchanges if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetrowmetadata') drop procedure sp_MSgetrowmetadata if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetmetadatabatch') drop procedure sp_MSgetmetadatabatch if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetrowmetadata') drop procedure sp_MSsetrowmetadata if exists (select * from sysobjects where type = 'P' and name = 'sp_MSinsertgenhistory') drop procedure sp_MSinsertgenhistory if exists (select * from sysobjects where type = 'P' and name = 'sp_MSupdategenhistory') drop procedure sp_MSupdategenhistory if object_id('sp_MSlocalizeinterruptedgenerations') is not null drop procedure sp_MSlocalizeinterruptedgenerations if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumschemachange') drop procedure sp_MSenumschemachange if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumschemachange_70') drop procedure sp_MSenumschemachange_70 if exists (select * from sysobjects where type = 'P' and name = 'sp_MSenumschemachange_80') drop procedure sp_MSenumschemachange_80 if exists (select * from sysobjects where type = 'P' and name = 'sp_MSupdateschemachange') drop procedure sp_MSupdateschemachange if exists (select * from sysobjects where type = 'P' and name = 'sp_MSadd_mergereplcommand') drop procedure sp_MSadd_mergereplcommand if exists (select * from sysobjects where type = 'P' and name = 'sp_MSremove_mergereplcommand') drop procedure sp_MSremove_mergereplcommand if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetreplicainfo') drop procedure sp_MSsetreplicainfo if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetreplicastatus') drop procedure sp_MSsetreplicastatus if exists (select * from sysobjects where type = 'P' and name = 'sp_MScreateglobalreplica') drop procedure sp_MScreateglobalreplica if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetconflictscript') drop procedure sp_MSsetconflictscript if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetconflicttable') drop procedure sp_MSsetconflicttable if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeconflictinsertproc') drop procedure sp_MSmakeconflictinsertproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmaketempinsertproc') drop procedure sp_MSmaketempinsertproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetconflictinsertproc') drop procedure sp_MSgetconflictinsertproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MSinsertdeleteconflict') drop procedure sp_MSinsertdeleteconflict if exists (select * from sysobjects where type = 'P' and name = 'sp_MScheckmetadatamatch') drop procedure sp_MScheckmetadatamatch if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdelrow') drop procedure sp_MSdelrow if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetartprocs') drop procedure sp_MSsetartprocs if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakesystableviews') drop procedure sp_MSmakesystableviews if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetchangecount') drop procedure sp_MSgetchangecount if exists (select * from sysobjects where type = 'P' and name = 'sp_MSuplineageversion') drop procedure sp_MSuplineageversion if exists (select * from sysobjects where type = 'P' and name = 'sp_MSvalidatearticle') drop procedure sp_MSvalidatearticle if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetviewcolumnlist') drop procedure sp_MSgetviewcolumnlist if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsubscriptionvalidated') drop procedure sp_MSsubscriptionvalidated if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdelsubrows') drop procedure sp_MSdelsubrows if exists (select * from sysobjects where type = 'P' and name = 'sp_MSdelsubrowsbatch') drop procedure sp_MSdelsubrowsbatch if exists (select * from sysobjects where type = 'P' and name = 'sp_MScontractsubsnb') drop procedure sp_MScontractsubsnb if exists (select * from sysobjects where type = 'P' and name = 'sp_MSexpandsubsnb') drop procedure sp_MSexpandsubsnb if exists (select * from sysobjects where type = 'P' and name = 'sp_MSmakeviewproc') drop procedure sp_MSmakeviewproc if exists (select * from sysobjects where type = 'P' and name = 'sp_MScreatebeforetable') drop procedure sp_MScreatebeforetable if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpcreatebeforetable') drop procedure sp_MShelpcreatebeforetable if exists (select * from sysobjects where type = 'P' and name = 'sp_MShelpalterbeforetable') drop procedure sp_MShelpalterbeforetable if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetbeforetableinsert') drop procedure sp_MSgetbeforetableinsert if exists (select * from sysobjects where type = 'P' and name = 'sp_MSfixupbeforeimagetables') drop procedure sp_MSfixupbeforeimagetables if exists (select * from sysobjects where type = 'P' and name = 'sp_MSinserterrorlineage') drop procedure sp_MSinserterrorlineage if exists (select * from sysobjects where type = 'P' and name = 'sp_MSevalsubscriberinfo') drop procedure sp_MSevalsubscriberinfo if exists (select * from sysobjects where type = 'P' and name = 'sp_MSsetsubscriberinfo') drop procedure sp_MSsetsubscriberinfo if exists (select * from sysobjects where type = 'P' and name = 'sp_MSgetsubscriberinfo') drop procedure sp_MSgetsubscriberinfo if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSinsertgenerationschemachanges') drop procedure sp_MSinsertgenerationschemachanges if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSalreadyhavegeneration') drop procedure sp_MSalreadyhavegeneration if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSgettablecontents') drop procedure sp_MSgettablecontents if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdelgenzero') drop procedure sp_MSdelgenzero if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSmakedynsnapshotvws') drop procedure sp_MSmakedynsnapshotvws if exists (select * from sysobjects where type = 'P ' and name = 'sp_MSdropdynsnapshotvws') drop procedure sp_MSdropdynsnapshotvws if object_id('dbo.sp_MSscriptviewproc', 'P') is not null drop procedure dbo.sp_MSscriptviewproc go exec dbo.sp_MS_marksystemobject sp_MSdrop_rlrecon go EXEC dbo.sp_MSdrop_rlrecon go raiserror('Creating procedure sp_MSfetchidentityrange', 0,1) GO CREATE PROCEDURE sp_MSfetchidentityrange @tablename sysname, @adjust_only bit AS declare @retcode int declare @objid int declare @distributor sysname declare @distribdb sysname declare @publisher sysname declare @publisher_db sysname declare @next_seed bigint declare @range bigint declare @threshold int declare @distproc nvarchar(300) declare @identity_support int select @publisher=@@SERVERNAME select @publisher_db=db_name() select @objid = object_id(@tablename) select @identity_support=identity_support from sysmergearticles where objid=@objid exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSfetchAdjustidentityrange' exec @retcode = @distproc @publisher=@publisher, @publisher_db=@publisher_db, @tablename=@tablename, @adjust_only=1, --do adjust only @next_seed=@next_seed OUTPUT, @range=@range OUTPUT, @threshold=@threshold OUTPUT IF @@ERROR <> 0 OR @retcode <> 0 return (1) --initialize article collection for agents. if @adjust_only=0 select @identity_support, @next_seed-range, range, threshold from MSrepl_identity_range where objid = @objid GO exec dbo.sp_MS_marksystemobject sp_MSfetchidentityrange go grant exec on dbo.sp_MSfetchidentityrange to public go raiserror('Creating procedure sp_MScheckidentityrange', 0,1) GO CREATE PROCEDURE sp_MScheckidentityrange @pubid uniqueidentifier, @artname sysname, @next_seed bigint, @range bigint, @threshold int, @checkonly int AS declare @colid int declare @colname sysname declare @retcode int declare @objid int declare @identity_so_far bigint declare @current_max bigint declare @max_identity bigint declare @tablename sysname declare @flag smallint declare @distributor sysname declare @distribdb sysname declare @republisher bit declare @publisher sysname declare @publisher_db sysname declare @distproc nvarchar(300) declare @pub_range bigint select @objid = objid from sysmergearticles where pubid=@pubid and name=@artname select @flag = 1 select @republisher = 0 select @tablename=object_name(@objid) if exists (select * from sysmergearticles where objid=@objid and pubid <>@pubid and pubid in (select pubid from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())) begin select @publisher=@@SERVERNAME select @publisher_db=db_name() select @republisher = 1 exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 begin raiserror(14071, 16, -1) return (1) end end if @checkonly=1 begin if @republisher=0 begin --current_max is defaulted to max_identity value if not republished at subscribers. select @current_max = current_max from MSrepl_identity_range where objid=@objid select @identity_so_far = ident_current(@tablename) if ident_incr(@tablename) < 0 select @flag = -1 if @flag * 100 * (@identity_so_far - (@current_max + 1 - @range))/@range > @threshold select 1 --needs bump up else select 0 --no need to bump up end else begin select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0, @max_identity=0 --make them non-NULL SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity' exec @retcode= @distproc @publisher=@@SERVERNAME, @publisher_db=@publisher_db, @tablename=@tablename, @range=@range OUTPUT, @max_identity=@max_identity OUTPUT, @next_seed = @next_seed OUTPUT, @pub_range=@pub_range OUTPUT if @retcode<>0 or @@ERROR<>0 begin raiserror(21195, 16, -1) return (1) end if (@max_identity-@next_seed)<@range or (@max_identity-@next_seed)<@pub_range select 1 else select 0 end end else begin if @republisher=0 begin --its current_max value is to be set by sp_addmergearticle, if to be republished. update MSrepl_identity_range set max_identity=@next_seed + @range, next_seed=@next_seed, current_max=@next_seed + @range -1 where objid = @objid exec sp_MSreseed @objid, @next_seed, @range if @@ERROR <> 0 goto FAILURE end else begin SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSfetchAdjustidentityrange' exec @retcode = @distproc @publisher=@publisher, @publisher_db=@publisher_db, @tablename=@tablename, @for_publisher = 2, --for repub case, increment max_identity at distributor side @next_seed = @next_seed, @range=@range OUTPUT, @adjust_only=1 --do adjust only if @retcode<>0 or @@ERROR<>0 begin raiserror(21315, 16, -1, @tablename) return (1) end end end return (0) FAILURE: select 0 return (1) go exec dbo.sp_MS_marksystemobject sp_MScheckidentityrange go grant exec on dbo.sp_MScheckidentityrange to public go raiserror('Creating procedure sp_MShelpmergeidentity', 0,1) GO CREATE PROCEDURE sp_MShelpmergeidentity @publication sysname as declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @nickname int declare @next_seed bigint declare @objid int declare @retcode int declare @tablename sysname declare @range bigint declare @threshold int declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @db_name sysname declare @tmp_table TABLE (tablename sysname, next_seed bigint, range bigint, threshold int, nickname int) /* ** To public. */ set nocount on if (@publication is null) begin RAISERROR(14003, 16, -1) return (1) end EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) select @db_name = db_name() select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name if (@pubid is null) begin RAISERROR (20026, 11, -1, @publication) return (1) end select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid and identity_support=1 order by nickname ASC while (@artid is not NULL) begin select @objid=objid, @nickname=nickname from sysmergearticles where pubid=@pubid and artid=@artid select @tablename=object_name(@objid) select @next_seed=0, @threshold=0, @range=0 --make them non-NULL SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity' exec @retcode= @distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@tablename, @range=@range OUTPUT, @threshold=@threshold OUTPUT, @next_seed = @next_seed OUTPUT if @retcode<>0 or @@ERROR<>0 return (1) insert @tmp_table values(@tablename, @next_seed, @range, @threshold, @nickname) if @@ERROR<>0 begin raiserror(21197, 16, -1) return (1) end select @artid = NULL select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid and nickname>@nickname and identity_support=1 order by nickname ASC end select * from @tmp_table order by nickname ASC return (0) go exec dbo.sp_MS_marksystemobject sp_MShelpmergeidentity go grant exec on dbo.sp_MShelpmergeidentity to public go raiserror('Creating procedure sp_MShelpmergearticles', 0,1) GO CREATE PROCEDURE sp_MShelpmergearticles @publication sysname, @compatibility_level int = 7000000, @pubidin uniqueidentifier = NULL as declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @user_name sysname declare @guid_col sysname declare @identity_support int declare @nickname int declare @identity_so_far bigint declare @next_seed bigint declare @pub_range bigint declare @objid int declare @qualname nvarchar(270) declare @retcode int declare @tablename sysname declare @range bigint declare @current_max bigint declare @threshold int declare @distributor sysname declare @distribdb sysname declare @distproc nvarchar(300) declare @flag smallint declare @c_max bigint declare @n_seed bigint declare @db_name sysname declare @has_joins int declare @article_filter_category int declare @haspartfilters int declare @grouppartfilterarticles int declare @injoinfilters int declare @nofilters int declare @objid_looper int declare @indexcol int declare @rowcount1 int declare @rowcount2 int declare @tmp_table TABLE (tablename sysname, user_name sysname, guid_col sysname NULL, next_seed bigint, range bigint, threshold int, artid uniqueidentifier, pubid uniqueidentifier, has_joins int, article_filter_category int, objid int, has_relation_with_joinarticles int default 0, node_visited bit default 0) declare @worktable TABLE (objid int NOT NULL, indexcol int) set @nofilters = 1 set @haspartfilters = 2 set @injoinfilters = 4 set @grouppartfilterarticles = 8 /* ** To public. */ set nocount on if (@publication is null) begin RAISERROR(14003, 16, -1) return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) select @db_name = db_name() if @pubidin is not NULL set @pubid = @pubidin else select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name if (@pubid is null) begin RAISERROR (20026, 11, -1, @publication) return (1) end select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid order by nickname ASC while (@artid is not NULL) begin select @objid=objid, @nickname=nickname, @identity_support=identity_support from sysmergearticles where pubid=@pubid and artid=@artid select @tablename=object_name(@objid) select @user_name=user_name(uid) from sysobjects where id=@objid select @qualname=QUOTENAME(@user_name) + '.' + QUOTENAME(@tablename) select @next_seed=NULL, @range=NULL, @threshold=NULL --null if not being updated later if @identity_support=1 and exists (select * from sysmergepublications where pubid = @pubid and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name) begin select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0 --make them non-NULL SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity' exec @retcode= @distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@tablename, @range=@range OUTPUT, @current_max=@current_max OUTPUT, @threshold=@threshold OUTPUT, @next_seed = @next_seed OUTPUT, @pub_range=@pub_range OUTPUT if @retcode<>0 or @@ERROR<>0 return (1) select @identity_so_far = IDENT_CURRENT(@tablename) select @flag=1 if ident_incr(@tablename) < 0 select @flag = -1 -- we attempted to adjust publisher side identity range based on its threshodl if needed -- however, non-dbo/sysadmin's will not be able to do so. -- so this is limited to dbos or sysadmins. if @flag * 100 * (@identity_so_far - (@current_max + 1 - @pub_range))/@pub_range > @threshold and ((is_srvrolemember('sysadmin') = 1) or (is_member('db_owner') = 1)) begin select @c_max=@next_seed + @pub_range - 1 select @n_seed=@next_seed + @pub_range SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity' exec @retcode=@distproc @publisher=@@SERVERNAME, @publisher_db=@db_name, @tablename=@tablename, @current_max=@c_max, @next_seed = @n_seed if @retcode<>0 or @@ERROR<>0 return (1) exec @retcode=sp_MSreseed @objid, @next_seed, @pub_range, 1 if @@ERROR <> 0 or @retcode<>0 begin raiserror(21197, 16, -1) return (1) end select @next_seed=@next_seed + @pub_range end end else if @identity_support=1 and not exists (select * from sysmergepublications where pubid = @pubid and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name) begin /* This is the case of a message based subscriber acting like publisher */ select @range = range, @threshold = threshold, @next_seed = next_seed from MSrepl_identity_range where objid = @objid end if exists (select * from sysmergesubsetfilters where join_nickname = @nickname) set @has_joins = 1 else set @has_joins = 0 -- reset for subsequent bitwise OR operations. set @article_filter_category = 0 if exists (select * from sysmergesubsetfilters where pubid = @pubid and (join_nickname = @nickname or art_nickname = @nickname)) set @article_filter_category = (@article_filter_category | @injoinfilters) if exists (select * from sysmergearticles where pubid = @pubid and nickname = @nickname and datalength(subset_filterclause) > 1) set @article_filter_category = (@article_filter_category | @haspartfilters | @grouppartfilterarticles) if (@article_filter_category = 0) set @article_filter_category = @nofilters select @guid_col=name from syscolumns where id=@objid and ColumnProperty(@objid, name, 'IsRowGuidCol')=1 insert @tmp_table values(@tablename, @user_name, @guid_col, @next_seed, @range, @threshold, @artid, @pubid, @has_joins, @article_filter_category, @objid, 0, 0) if @@ERROR<>0 begin raiserror(21197, 16, -1) return (1) end select @artid = NULL select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid and nickname>@nickname order by nickname ASC end -- Find articles that have no relations (in either direction - referring or referenced) with any articles in the join filter category. if exists (select * from @tmp_table where (article_filter_category & @injoinfilters) = @injoinfilters) begin --create unique index #uncworktable on @worktable(objid, indexcol) update @tmp_table set node_visited = 1 where objid not in (select fkeyid from sysreferences) and objid not in (select rkeyid from sysreferences) and (article_filter_category & @injoinfilters) = 0 -- get the min objid from the temp table of all articles select @objid_looper = min(objid) from @tmp_table where node_visited = 0 while (@objid_looper is not null) begin delete from @worktable select @indexcol = 0 insert into @worktable values (@objid_looper, @indexcol) -- find all the objects referenced by this object and all objects that reference this object. insert into @worktable select distinct rkeyid, @indexcol+1 from sysreferences where fkeyid = @objid_looper and rkeyid not in (select objid from @worktable) select @rowcount1 = @@rowcount insert into @worktable select distinct fkeyid, @indexcol+1 from sysreferences where rkeyid = @objid_looper and fkeyid not in (select objid from @worktable) select @rowcount2 = @@rowcount while (@rowcount1 <> 0 or @rowcount2 <> 0) begin select @indexcol = @indexcol+1 insert into @worktable select distinct s.rkeyid, @indexcol+1 from sysreferences s, @worktable w, @tmp_table t where s.fkeyid = w.objid and w.objid = t.objid and w.indexcol = @indexcol and t.node_visited = 0 and s.rkeyid not in (select objid from @worktable) select @rowcount1 = @@rowcount insert into @worktable select distinct s.fkeyid, @indexcol+1 from sysreferences s, @worktable w, @tmp_table t where s.rkeyid = w.objid and w.objid = t.objid and w.indexcol = @indexcol and t.node_visited = 0 and s.fkeyid not in (select objid from @worktable) select @rowcount2 = @@rowcount end if exists (select * from @worktable w, @tmp_table t where w.objid = t.objid and (t.article_filter_category & @injoinfilters) = @injoinfilters) begin -- all articles in @worktable have a direct or indirect relation with at least one object in the join articles category. update @tmp_table set node_visited = 1, has_relation_with_joinarticles = 1 from @tmp_table t, @worktable w where w.objid = t.objid end else begin -- none of the articles in @worktable has any relation (direct or indirect) with any article in the join articles category. update @tmp_table set node_visited = 1, has_relation_with_joinarticles = 0 from @tmp_table t, @worktable w where w.objid = t.objid end -- process more unvisited articles from @tmp_table select @objid_looper = min(objid) from @tmp_table where objid > @objid_looper and node_visited = 0 end end /* If the 7.0 merge agent is making this call then we need to make sure that the CLSID of the sp resolver is the old one and not the new one */ if @compatibility_level = 7000000 begin begin transaction update sysmergearticles set resolver_clsid = '{6F31CE30-7BE4-11d1-9B0A-00C04FC2DEB3}' where article_resolver = 'Microsoft SQLServer Stored Procedure Resolver' select name, t.tablename, t.user_name, a.artid, pre_creation_command, a.pubid, nickname, column_tracking, status, resolver_clsid, conflict_script, conflict_table, insert_proc, update_proc, select_proc, destination_object, missing_col_count, missing_cols, t.guid_col, article_resolver, resolver_info, subset_filterclause, has_joins, excluded_col_count, excluded_cols, destination_owner, identity_support, t.next_seed, t.range, t.threshold, verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions, t.article_filter_category, t.has_relation_with_joinarticles from sysmergearticles a, @tmp_table t where a.artid=t.artid and a.pubid=t.pubid order by a.nickname rollback transaction end else begin select name, t.tablename, t.user_name, a.artid, pre_creation_command, a.pubid, nickname, column_tracking, status, resolver_clsid, conflict_script, conflict_table, insert_proc, update_proc, select_proc, destination_object, missing_col_count, missing_cols, t.guid_col, article_resolver, resolver_info, subset_filterclause, has_joins, excluded_col_count, excluded_cols, destination_owner, identity_support, t.next_seed, t.range, t.threshold, verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions, t.article_filter_category, t.has_relation_with_joinarticles from sysmergearticles a, @tmp_table t where a.artid=t.artid and a.pubid=t.pubid order by a.nickname end return (0) go exec dbo.sp_MS_marksystemobject sp_MShelpmergearticles go grant exec on dbo.sp_MShelpmergearticles to public go raiserror('Creating procedure sp_MShelpmergeschemaarticles', 0,1) go CREATE PROCEDURE sp_MShelpmergeschemaarticles @publication sysname as begin set nocount on declare @pubid uniqueidentifier declare @db_name sysname if (@publication is null) begin raiserror(14003, 16, -1) return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin raiserror(20054, 16, -1) return (1) end select @db_name = db_name() select @pubid = pubid from dbo.sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db = @db_name if (@pubid is null) begin raiserror(20026, 11, -1, @publication) return (1) end select a.name, o.name, user_name(o.uid), a.destination_object, a.destination_owner, a.artid, @pubid, a.pre_creation_command, a.status, a.type from dbo.sysmergeschemaarticles a inner join sysobjects o on a.objid = o.id where pubid = @pubid return 0 end go exec dbo.sp_MS_marksystemobject sp_MShelpmergeschemaarticles go grant exec on dbo.sp_MShelpmergeschemaarticles to public go raiserror('Creating procedure sp_MScreateretry', 0,1) GO CREATE PROCEDURE sp_MScreateretry as declare @tname sysname declare @pname sysname declare @tempname sysname declare @guid uniqueidentifier declare @guidstr varchar(40) declare @retcode smallint /* ** To public. */ set @guid = newid() exec @retcode=sp_MSguidtostr @guid, @guidstr out if @retcode<>0 or @@ERROR<>0 return (1) set @tempname = '##retry_' + @guidstr exec @retcode = dbo.sp_MSuniquetempname @tempname, @tempname out if (@@error <> 0) OR @retcode <> 0 begin RAISERROR(15001, 16, -1, 'sp_MSuniquetempname') return (1) end exec ('create table ' + @tempname + ' (tablenick int NOT NULL, rowguid uniqueidentifier ROWGUIDCOL default newid() not null, errcode int NOT NULL, errtext nvarchar(255) NULL, type tinyint NOT NULL)' ) if (@@error <> 0) begin RAISERROR(15001, 16, -1, @tempname) return (1) end set @tname = @tempname set @tempname = '##insert_' + @guidstr exec @retcode = dbo.sp_MSuniquetempname @tempname, @tempname out if (@@error <> 0) begin RAISERROR(15001, 16, -1, 'sp_MSuniquetempname') return (1) end exec @retcode = dbo.sp_MSmaketempinsertproc @tname, @tempname if @@ERROR <>0 or @retcode<>0 return (1) select @pname = @tempname select @tname, @pname return (0) go exec dbo.sp_MS_marksystemobject sp_MScreateretry go grant exec on dbo.sp_MScreateretry to public raiserror('Creating procedure sp_MSdropretry', 0,1) GO CREATE PROCEDURE sp_MSdropretry (@tname sysname, @pname sysname) as declare @retcode int /* ** To public */ exec ('drop table ' + @tname) if @@ERROR <> 0 return(1) exec ('drop procedure ' + @pname) if @@ERROR <> 0 return(1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSdropretry go grant exec on dbo.sp_MSdropretry to public go raiserror('Creating procedure sp_MSdroptemptable', 0,1) GO CREATE PROCEDURE sp_MSdroptemptable (@tname sysname) as if exists(select * from tempdb.dbo.sysobjects where name = @tname) begin exec ('drop table ' + @tname) if @@ERROR <> 0 return(1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSdroptemptable go grant exec on dbo.sp_MSdroptemptable to public go raiserror('Creating procedure sp_MSchangearticleresolver', 0,1) GO CREATE PROCEDURE sp_MSchangearticleresolver ( @article_resolver nvarchar(255), @resolver_clsid nvarchar(40), @artid uniqueidentifier, @resolver_info sysname = NULL ) as if (@resolver_clsid='') select @resolver_clsid = NULL UPDATE sysmergearticles SET article_resolver = @article_resolver, resolver_clsid = @resolver_clsid, resolver_info = @resolver_info WHERE artid = @artid if @@ERROR <> 0 RETURN (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSchangearticleresolver go -- **************************************************** -- THIS COMMENTED CODE SECTION WILL BE DELETED SOON -- sp_MSgetversion is now part of XPSTAR.DLL and -- will not be installed by REPL code --raiserror('Creating sp_MSgetversion', 0,1) --GO if not exists (select * from sysobjects where name = 'sp_MSgetversion') begin exec dbo.sp_addextendedproc 'sp_MSgetversion', 'xpstar.dll' exec dbo.sp_MS_marksystemobject sp_MSgetversion grant exec on dbo.sp_MSgetversion to public end go -- **************************************************** raiserror('Creating procedure sp_MSenumretries', 0,1) GO CREATE PROCEDURE sp_MSenumretries (@tname nvarchar(386), @maxrows int, @tablenick int, @rowguid uniqueidentifier) as declare @tnstring nvarchar(12) declare @rgstring nvarchar(38) declare @retcode int /* ** Modify temp table, granted to public. */ declare @selecttop nvarchar(20) if (@maxrows = 0) set @selecttop= 'select' else set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) if (@tablenick < 1) begin execute (@selecttop + ' tablenick, rowguidcol, errcode, errtext, type from ' + @tname + ' order by tablenick, rowguidcol') IF @@ERROR <>0 RETURN (1) end else begin set @tnstring = convert(nchar, @tablenick) set @rgstring = '''' + convert(nchar(36), @rowguid) + '''' execute (@selecttop + ' tablenick, rowguidcol, errcode, errtext, type from ' + @tname + ' where (tablenick = ' + @tnstring + ' and rowguidcol > ' + @rgstring + ') or tablenick > ' + @tnstring + ' order by tablenick, rowguidcol' ) if @@ERROR <> 0 RETURN (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumretries go grant exec on dbo.sp_MSenumretries to public raiserror('Creating procedure sp_MSdeleteretry', 0,1) GO CREATE PROCEDURE sp_MSdeleteretry (@temptable nvarchar(386), @tablenick int, @rowguid uniqueidentifier) as declare @guidstr nvarchar(38) declare @nickstr nvarchar(12) declare @retcode int /* ** Modify temp table, granted to public. */ set @nickstr = convert(nchar, @tablenick) set @guidstr = '''' + convert(nchar(36), @rowguid) + '''' execute ('delete from ' + @temptable + ' where tablenick = ' + @nickstr + ' and rowguidcol = ' + @guidstr) IF @@ERROR <>0 RETURN (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSdeleteretry go grant exec on dbo.sp_MSdeleteretry to public go raiserror('Creating procedure sp_MSdeletepushagent', 0,1) GO /* ** This procedure is obselete for dropping push agent at distribution database. ** If we were to recover, don't use server id as parameter for RPC into distributor. ** Use server name instead. */ CREATE PROCEDURE sp_MSdeletepushagent ( @publisher sysname, @publisher_db sysname, @publication sysname, @subscriber sysname, @subscriber_db sysname ) AS declare @distributor sysname declare @distribdb sysname declare @pubid uniqueidentifier declare @distproc nvarchar(300) declare @pub_srvid smallint declare @sub_srvid smallint declare @retcode smallint EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb = @distribdb OUTPUT IF @@ERROR <> 0 or @retcode <> 0 return (1) select @pub_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default select @sub_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSdrop_agent_entry' EXEC @retcode = @distproc @pub_srvid, @publisher_db, @publication, @sub_srvid, @subscriber_db IF @@ERROR <> 0 OR @retcode <> 0 return (1) return (0) GO exec dbo.sp_MS_marksystemobject sp_MSdeletepushagent go grant exec on dbo.sp_MSdeletepushagent to public go raiserror('Creating procedure sp_MSgetonerow', 0,1) GO CREATE PROCEDURE sp_MSgetonerow (@tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL) as declare @retcode smallint declare @procname sysname /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (1) end select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid=@pubid exec @retcode = @procname @type =1, @rowguid=@rowguid IF @@ERROR<>0 or @retcode<>0 RETURN (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetonerow go grant exec on dbo.sp_MSgetonerow to public go raiserror('Creating procedure sp_MSuplineageversion', 0,1) GO CREATE PROCEDURE sp_MSuplineageversion (@tablenick int, @rowguid uniqueidentifier, @version int) as declare @replnick int declare @curversion int declare @lineage varbinary(255) declare @retcode int declare @colv varbinary(2048) declare @col_tracking int declare @rowintombstone int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (1) end exec dbo.sp_MSgetreplnick @nickname = @replnick out if (@@error <> 0) or @replnick IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick begin transaction save tran sp_MSuplineageversion -- get lineage, locking row in MSmerge_contents. If not found there, also try MSmerge_tombstone set @rowintombstone= 0 select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and rowguid = @rowguid if (@lineage is null) begin select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and rowguid = @rowguid set @colv= NULL set @rowintombstone= 1 end if (@lineage is null) begin RAISERROR(14043, 16, -1, '@lineage') goto Failure end set @curversion = 0 while (@curversion < @version) begin set @lineage= { fn UPDATELINEAGE(@lineage, @replnick, 1) } set @curversion= { fn GETMAXVERSION(@lineage) } IF @@ERROR<>0 goto Failure end if (@col_tracking = 0 or @colv is NULL) set @colv = NULL else set @colv = { fn UPDATECOLVBM(@colv, @replnick, 0x01, 0x00, @curversion) } -- update lineage in MSmerge_contents or MSmerge_tombstone if (@rowintombstone = 0) begin update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv where tablenick = @tablenick and rowguid = @rowguid end else begin update dbo.MSmerge_tombstone set lineage = @lineage where tablenick = @tablenick and rowguid = @rowguid end commit return (0) Failure: rollback tran sp_MSuplineageversion commit tran return(1) go exec dbo.sp_MS_marksystemobject sp_MSuplineageversion go grant exec on dbo.sp_MSuplineageversion to public raiserror('Creating procedure sp_MSgetlastrecgen', 0,1) GO CREATE PROCEDURE sp_MSgetlastrecgen (@repid uniqueidentifier) as declare @pubid uniqueidentifier declare @pubname sysname declare @status int declare @retcode int if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end if (@repid is null) begin RAISERROR(14043, 16, -1, '@repid') return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ select @pubid = pubid from sysmergesubscriptions where subid = @repid if (@pubid is not null) begin EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END end select recgen, recguid from MSmerge_replinfo where repid = @repid return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetlastrecgen go grant exec on dbo.sp_MSgetlastrecgen to public raiserror('Creating procedure sp_MSgetlastsentgen', 0,1) GO CREATE PROCEDURE sp_MSgetlastsentgen (@repid uniqueidentifier) as declare @pubid uniqueidentifier declare @pubname sysname declare @status int declare @retcode int if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end if (@repid is null) begin RAISERROR(14043, 16, -1, '@repid') return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ select @pubid = pubid from sysmergesubscriptions where subid = @repid if (@pubid is not null) begin EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END end select sentgen, sentguid from MSmerge_replinfo where repid = @repid return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetlastsentgen go grant exec on dbo.sp_MSgetlastsentgen to public go raiserror('Creating procedure sp_MSgetlastsentrecgens', 0,1) GO CREATE PROCEDURE sp_MSgetlastsentrecgens (@repid uniqueidentifier) as declare @pubid uniqueidentifier declare @pubname sysname declare @status int declare @retcode int if (@repid is null) begin RAISERROR(14043, 16, -1, '@repid') return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ select @pubid = pubid from sysmergesubscriptions where subid = @repid if (@pubid is not null) begin EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END end select sentgen, sentguid, recgen, recguid from MSmerge_replinfo where repid = @repid return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetlastsentrecgens go grant exec on dbo.sp_MSgetlastsentrecgens to public raiserror('Creating procedure sp_MSdummyupdate', 0,1) GO CREATE PROCEDURE sp_MSdummyupdate (@rowguid uniqueidentifier, @tablenick int, @metatype tinyint, @pubid uniqueidentifier = NULL, @uplineage tinyint = 1, @inlineage varbinary(255) = NULL, @incolv varbinary(2048) = NULL) as declare @retcode int declare @lineage varbinary(255) declare @conflict_lineage varbinary(255) declare @mynickname int declare @col_tracking int declare @colv varbinary(2048) declare @reason nvarchar(255) declare @oldmaxversion int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (1) end /* Parameter checks */ if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@metatype is null) begin RAISERROR(14043, 16, -1, '@metatype') return (1) end /* Check if we have a merge publication by whether system table is there */ if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end exec dbo.sp_MSgetreplnick @nickname = @mynickname out if (@@error <> 0) or @mynickname IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end -- Look for the "other" lineage in a conflict table select @conflict_lineage = max(lineage) from MSmerge_errorlineage where rowguid = @rowguid and tablenick = @tablenick set @oldmaxversion= (select top 1 maxversion_at_cleanup from sysmergearticles where nickname = @tablenick) if (@metatype = 0) begin /* We don't have the row. Putting in a system delete tombstone should cause a delete and ** eventual convergence. We are already logging the row as a conflict / error. */ if @conflict_lineage is not null begin set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) } end else begin set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) } end select @reason = formatmessage(20564) -- system delete insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) values (@rowguid, @tablenick, 6, @lineage, 0, @reason) end else if (@metatype = 1) begin if @inlineage is not null begin set @lineage = @inlineage set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) } end else begin select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and rowguid = @rowguid if (@uplineage = 1) begin if @conflict_lineage is not null begin exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output if @@error<>0 or @retcode<>0 return(1) end set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) } end end update dbo.MSmerge_tombstone set generation = 0, lineage = @lineage where tablenick = @tablenick and rowguid = @rowguid end else if (@metatype = 2) begin if @inlineage is not null begin set @lineage = @inlineage set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) } if @incolv is not null begin set @colv = @incolv end else begin select @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and rowguid = @rowguid if @pubid is NULL select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick else select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid if (@col_tracking = 0 or @colv is NULL) set @colv = NULL else set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) } end end else -- @inlineage is null -> @incolv is null, too begin select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and rowguid = @rowguid if (@uplineage = 1) begin if @conflict_lineage is not null begin exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output if @@error<>0 or @retcode<>0 return(1) end set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) } if @pubid is NULL select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick else select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid if (@col_tracking = 0 or @colv is NULL) set @colv = NULL else set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) } end end update dbo.MSmerge_contents set generation = 0, lineage = @lineage, colv1 = @colv where tablenick = @tablenick and rowguid = @rowguid end else if (@metatype = 3) begin declare @ccols int declare @sync_objid int declare @missing_count int if @inlineage is not null begin set @lineage = @inlineage set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) } end else if @conflict_lineage is not null begin set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) } end else begin set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) } end if @incolv is not null begin set @colv = @incolv end else begin if @pubid is NULL begin select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count from sysmergearticles where nickname = @tablenick end else begin select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count from sysmergearticles where nickname = @tablenick and pubid = @pubid end if (@col_tracking = 0) set @colv = NULL else begin select @ccols= count(*) from syscolumns where id = @sync_objid set @ccols = @ccols + @missing_count set @colv = { fn INITCOLVS(@ccols, @mynickname ) } end end insert into dbo.MSmerge_contents (tablenick, rowguid, lineage, generation, colv1) values (@tablenick, @rowguid, @lineage, 0, @colv) end else if (@metatype = 6) -- e.g., used to cope with dup key / dup index begin if @conflict_lineage is not null begin set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) } end else begin set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) } end select @reason = formatmessage(20564) -- system delete begin transaction if not exists (select * from MSmerge_tombstone where rowguid=@rowguid and tablenick=@tablenick) begin insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) values (@rowguid, @tablenick, @metatype, @lineage, 0, @reason) end delete from dbo.MSmerge_contents where rowguid=@rowguid and tablenick=@tablenick commit end return (0) go exec dbo.sp_MS_marksystemobject sp_MSdummyupdate go grant exec on dbo.sp_MSdummyupdate to public go raiserror('Creating procedure sp_MSsetlastrecgen', 0,1) GO CREATE PROCEDURE sp_MSsetlastrecgen (@repid uniqueidentifier, @srcgen int, @srcguid uniqueidentifier) as /* ** Check to see if current publication has permission */ declare @retcode int declare @pubid uniqueidentifier declare @pubname sysname declare @status int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @repid = @repid if @retcode<>0 or @@ERROR<>0 return (1) end if (@repid is null) begin RAISERROR(14043, 16, -1, '@repid') return (1) end if (@srcgen is null) begin RAISERROR(14043, 16, -1, '@srcgen') return (1) end if (@srcguid is null) begin RAISERROR(14043, 16, -1, '@srcguid') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ select @pubid = pubid from sysmergesubscriptions where subid = @repid if (@pubid is not null) begin EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END end update MSmerge_replinfo set recgen = @srcgen, recguid = @srcguid where repid = @repid IF @@ERROR <>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetlastrecgen go grant exec on dbo.sp_MSsetlastrecgen to public go dump tran master with no_log go raiserror('Creating procedure sp_MSsetlastsentgen', 0,1) GO CREATE PROCEDURE sp_MSsetlastsentgen (@repid uniqueidentifier, @srcgen int, @srcguid uniqueidentifier) as /* ** Check to see if current publication has permission */ declare @retcode int declare @pubid uniqueidentifier declare @pubname sysname declare @status int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @repid = @repid if @retcode<>0 or @@ERROR<>0 return (1) end if (@repid is null) begin RAISERROR(14043, 16, -1, '@repid') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ select @pubid = pubid from sysmergesubscriptions where subid = @repid if (@pubid is not null) begin EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END end /* ** This is a special case which is used to update the anonymous subscriptions sent and ** received generation guids at the end of initializing a dynamic subscription. This is ** used in the case where an anonymous subscription is reinitialized after a merge metadata ** cleanup */ if (@srcgen is null and @srcguid is null) begin declare @minnullgen int declare @lastrecsentgen int declare @lastrecsentguid uniqueidentifier select @minnullgen = min(generation) from dbo.MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000' if @minnullgen IS NOT NULL select @lastrecsentgen = max(generation) from dbo.MSmerge_genhistory where generation < @minnullgen if @lastrecsentgen IS NOT NULL select @lastrecsentguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastrecsentgen update MSmerge_replinfo set sentgen= @lastrecsentgen, sentguid = @lastrecsentguid, recgen = @lastrecsentgen, recguid = @lastrecsentguid where repid = @repid IF @@ERROR <>0 return (1) end else begin -- check for setting a sentgen which is obviously too high if (exists (select * from dbo.MSmerge_genhistory where generation < @srcgen and guidlocal = '00000000-0000-0000-0000-000000000000' and (art_nick = 0 or art_nick is null or art_nick in (select nickname from sysmergearticles where pubid = @pubid) ))) begin RAISERROR('Setting sentgen too high', 16, -1) return (1) end update MSmerge_replinfo set sentgen= @srcgen, sentguid = @srcguid where repid = @repid IF @@ERROR <>0 return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetlastsentgen go grant exec on dbo.sp_MSsetlastsentgen to public go raiserror('Creating procedure sp_MSenumgenerations', 0,1) GO CREATE PROCEDURE sp_MSenumgenerations (@genstart int, @pubid uniqueidentifier, @return_count_of_generations bit = 0) as declare @retcode smallint declare @guidnull uniqueidentifier declare @generation_range TABLE (generation int NOT NULL, guidsrc uniqueidentifier NOT NULL, art_nick int NULL, guidlocal uniqueidentifier NOT NULL, pubid uniqueidentifier NULL, nicknames varbinary(1000) NOT NULL, okaytoskip bit NOT NULL) declare @status int declare @pubname sysname declare @rowcount int set @guidnull = '00000000-0000-0000-0000-000000000000' /* ** To public */ if (@genstart is null) begin RAISERROR(14043, 16, -1, '@genstart') return (1) end if (@pubid is null) begin RAISERROR(14043, 16, -1, '@pubid') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end /* ** Check the publication status at the publisher - if it is inactive ( say because of a ** metadata cleanup event then return an error so the current merge will quit. */ EXEC @retcode = dbo.sp_MScheckatpublisher @pubid IF @retcode = 0 BEGIN select @pubname = name, @status = status from sysmergepublications where pubid = @pubid if @status = 0 begin RAISERROR(21505, 16, -1, @pubname) return (1) end END insert into @generation_range (generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip) select DISTINCT generation, guidsrc, art_nick, guidlocal, pubid, nicknames, 0 from dbo.MSmerge_genhistory where generation >= @genstart and (art_nick = 0 or art_nick is NULL or art_nick in (select nickname from sysmergearticles where pubid = @pubid)) select @rowcount = @@rowcount if (@return_count_of_generations = 1) select @rowcount -- optimizations -- 1. skip all rows that are for incomplete generations for articles that have no joins. -- 2. skip all rows for join articles if all the join article rows are incomplete generations. update @generation_range set okaytoskip = 1 where art_nick is not null and art_nick <> 0 and guidlocal = @guidnull and ( ( -- 1. skip all rows that are for incomplete generations for articles that have no joins. not exists (select 1 from sysmergesubsetfilters where join_nickname = art_nick or art_nickname = art_nick) ) or ( -- 2. skip all rows for join articles if all the rows for join and joined articles (i.e. the articles represented by join_nickname -- and art_nickname in sysmergesubsetfilters) are incomplete generations. art_nick in (select join_nickname from sysmergesubsetfilters) and not exists ( select 1 from @generation_range b where b.guidlocal <> @guidnull and exists (select 1 from sysmergesubsetfilters where join_nickname = b.art_nick or art_nickname = b.art_nick) and b.generation > @genstart ) ) ) select generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip from @generation_range ORDER BY generation ASC return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumgenerations go grant exec on dbo.sp_MSenumgenerations to public go raiserror('Creating procedure sp_MScheckexistsgeneration', 0,1) GO CREATE PROCEDURE sp_MScheckexistsgeneration (@genguid uniqueidentifier, @gen int output, @pubid uniqueidentifier = NULL) as /* ** Check input parameter */ if (@genguid is null) begin RAISERROR(14043, 16, -1, '@genguid') return (1) end /* Normal case : do not qualify by pubid */ if (@pubid IS NULL) select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @genguid and guidlocal <> '00000000-0000-0000-0000-000000000000' else /* If we are reinitializing from an alternate publisher, check if the subscription has received generations for the alternate publication */ select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @genguid and guidlocal <> '00000000-0000-0000-0000-000000000000' and ((pubid = @pubid) or (pubid is null)) IF @@ERROR <>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MScheckexistsgeneration go grant exec on dbo.sp_MScheckexistsgeneration to public GO CREATE PROCEDURE sp_MSchecksnapshotstatus @publication sysname AS declare @db_name sysname declare @retention int declare @snapshot_ready int declare @pubid uniqueidentifier declare @last_snapshot datetime select @snapshot_ready = NULL select @db_name = db_name() select @snapshot_ready=snapshot_ready, @retention=retention, @pubid=pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=@db_name if @snapshot_ready is NULL select @snapshot_ready=snapshot_ready, @retention=retention, @pubid=pubid from sysmergepublications where name=@publication if @snapshot_ready is NULL begin raiserror (20026, 11, -1, @publication) return (1) end if @snapshot_ready=1 and @retention>0 begin select @last_snapshot=last_validated from sysmergesubscriptions where subid=@pubid if dateadd(day, @retention, @last_snapshot)<getdate() select @snapshot_ready=3 /* snapshot is obsolete */ end select @snapshot_ready go exec dbo.sp_MS_marksystemobject sp_MSchecksnapshotstatus go grant exec on dbo.sp_MSchecksnapshotstatus to public raiserror('Creating procedure sp_MSenumreplicas', 0,1) GO CREATE PROCEDURE sp_MSenumreplicas (@pubid uniqueidentifier) as declare @inactive tinyint /* ** To public */ select @inactive = 0 if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end select subs.subid, replinfo.replnickname, subs.subscriber_type, subs.subscription_type, subs.priority, replinfo.schemaversion, replinfo.schemaguid, subs.datasource_type, subs.datasource_path, servers.srvname, subs.db_name, subs.status, subs.partnerid, subs.sync_type, subs.description, subs.pubid, subs.publication, subs.distributor from sysmergesubscriptions subs, MSmerge_replinfo replinfo, master..sysservers servers where replinfo.repid = subs.subid and subs.srvid = servers.srvid and subs.status <> @inactive and subs.subscriber_type = 1 order by convert(binary, subs.subid) IF @@ERROR <>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumreplicas go grant exec on dbo.sp_MSenumreplicas to public raiserror('Creating procedure sp_MSenumdeletesmetadata', 0,1) GO CREATE PROCEDURE sp_MSenumdeletesmetadata( @pubid uniqueidentifier, @maxrows int, @genlist varchar(8000), @tablenick int, @rowguid uniqueidentifier, @filter_partialdeletes int = 0, @specified_article_only int = 0, @mingen int = 0, @maxgen int = 0) as declare @tnstring nvarchar(12) declare @rgstring nvarchar(38) declare @pubidstr nvarchar(38) declare @tablenick_qual nvarchar(100) declare @maxgenmingen_clause nvarchar(100) /* ** To public. */ if (@genlist is null) begin RAISERROR(14043, 16, -1, '@genlist') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' /* If the filter_partialdeletes is NOT set, include all types of tombstones, else filter the type = 5 ones */ if (@filter_partialdeletes = 0) set @tablenick_qual = ' and ts.tablenick = sm.nickname ' else set @tablenick_qual = ' and ts.tablenick = sm.nickname and ts.type <> 5' declare @selecttop nvarchar(20) if (@maxrows = 0) set @selecttop= 'select' else set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) declare @mingenstr nvarchar(13) declare @maxgenstr nvarchar(13) select @mingenstr = convert(nchar, @mingen) select @maxgenstr = convert(nchar, @maxgen) if (@maxgen = 0) select @maxgenmingen_clause = ' ' else select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and ' if (@tablenick = 0) begin if (@genlist is not null and rtrim(ltrim(@genlist)) <> '') begin execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and sm.pubid = ' + @pubidstr + @tablenick_qual + ' order by tablenick desc, rowguid asc' ) IF @@ERROR <>0 begin return (1) end end end else begin set @tnstring = convert(nchar, @tablenick) set @rgstring = '''' + convert(nchar(36), @rowguid) + '''' if (@genlist is not null and rtrim(ltrim(@genlist)) <> '') begin if (@specified_article_only = 1) begin execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ' and sm.pubid = ' + @pubidstr + @tablenick_qual + ' order by rowguid' ) IF @@ERROR <>0 begin return (1) end end else begin execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and ((tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ') or tablenick < ' + @tnstring + ') and sm.pubid = ' + @pubidstr + @tablenick_qual + ' order by tablenick desc, rowguid asc' ) IF @@ERROR <>0 begin return (1) end end end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumdeletesmetadata go grant exec on dbo.sp_MSenumdeletesmetadata to public go raiserror('Creating procedure sp_MSenumpartialdeletes', 0,1) GO CREATE PROCEDURE sp_MSenumpartialdeletes (@maxrows int, @tablenick int, @rowguid uniqueidentifier, @tablenotbelongs nvarchar(255), @bookmark int = NULL, @specified_article_only int = 0) as declare @tnstring nvarchar(12) declare @rgstring nvarchar(38) declare @lowrangestr nvarchar(12) declare @highrangestr nvarchar(12) /* ** To public. */ declare @selecttop nvarchar(20) if (@maxrows = 0) set @selecttop= 'select' else set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) if (@tablenick < 1) begin execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + ' order by tablenick desc, rowguid asc' ) IF @@ERROR <>0 begin return (1) end end else begin set @tnstring = convert(nchar, @tablenick) set @rgstring = '''' + convert(nchar(36), @rowguid) + '''' /* ** If a 7.0 SP1 Merge agent is calling this sp, it passed a valid bookmark parameter ** Use the bookmark column in the ##belongs_<> table to retrieve the next batch of rows */ if @bookmark is NOT NULL begin set @lowrangestr = convert(nchar, @bookmark) set @highrangestr = convert(nchar, (@bookmark + @maxrows)) if (@specified_article_only = 1) begin -- don't rely on @highrangestr since it will be equal to @lowrangestr if @maxrows=0. execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + ' where tablenick = ' + @tnstring + ' and bookmark > ' + @lowrangestr + ' order by rowguid' ) if @@ERROR<>0 begin return (1) end end else begin execute ('select tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + ' where ((tablenick = ' + @tnstring + ' and bookmark > ' + @lowrangestr + ' and bookmark <= ' + @highrangestr + ') or tablenick < ' + @tnstring + ') order by tablenick desc, rowguid asc' ) if @@ERROR<>0 begin return (1) end end end /* ** Backward compatibilty mode : If a 7.0 Merge agent is calling this sp, it will pass a NULL bookmark parameter ** Use the rowguid and set rowcount to retrieve the next batch of rows */ else begin if (@specified_article_only = 1) begin execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type from ' + @tablenotbelongs + ' where tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ' order by rowguid' ) IF @@ERROR <>0 begin return (1) end end else begin execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type from ' + @tablenotbelongs + ' where ((tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ') or tablenick < ' + @tnstring + ') order by tablenick desc, rowguid asc' ) IF @@ERROR <>0 begin return (1) end end end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumpartialdeletes go grant exec on dbo.sp_MSenumpartialdeletes to public go raiserror('Creating procedure sp_MSenumchanges', 0,1) GO CREATE PROCEDURE sp_MSenumchanges (@maxrows int, @genlist varchar(8000), @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL, @oldmaxgen int=0, @mingen int = 0, @maxgen int = 0) as declare @tnstring nvarchar(12) declare @oldmaxgenstr nvarchar(12) declare @rgstring nvarchar(38) declare @retcode smallint declare @procname nvarchar(270) declare @maxgenmingen_clause nvarchar(100) /* ** To public. */ if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@genlist is null) begin RAISERROR(14043, 16, -1, '@genlist') return (1) end create table #cont (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(249) NULL , colv1 varbinary(2048) NULL) set @tnstring = convert(nchar, @tablenick) set @rgstring = '''' + convert(nchar(36), @rowguid) + '''' declare @selecttop nvarchar(20) if (@maxrows = 0) set @selecttop= 'select' else set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) declare @mingenstr nvarchar(13) declare @maxgenstr nvarchar(13) select @mingenstr = convert(nchar, @mingen) select @maxgenstr = convert(nchar, @maxgen) if (@maxgen = 0) select @maxgenmingen_clause = ' ' else select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and ' if (@genlist is not null and rtrim(ltrim(@genlist)) <> '') begin if @oldmaxgen > 0 begin set @oldmaxgenstr = convert(nchar, @oldmaxgen) execute ('insert into #cont ' + @selecttop + ' tablenick, rowguid, generation, lineage, colv1 from dbo.MSmerge_contents where ( ( ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') ) or generation = 0 or generation > ' + @oldmaxgenstr + ' ) and tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ' order by rowguid' ) end else execute ('insert into #cont ' + @selecttop + ' tablenick, rowguid, generation, lineage, colv1 from dbo.MSmerge_contents where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ' order by rowguid' ) if @@ERROR <>0 begin return (1) end end select @procname = select_proc from sysmergearticles where nickname=@tablenick and pubid = @pubid exec @retcode = @procname @type=2 IF @@ERROR<>0 or @retcode<>0 begin RETURN (1) end drop table #cont return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumchanges go grant exec on dbo.sp_MSenumchanges to public go raiserror('Creating procedure sp_MSenumpartialchanges', 0,1) GO CREATE PROCEDURE sp_MSenumpartialchanges (@maxrows int, @temp_cont sysname, @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL) as declare @retcode smallint declare @tnstring nvarchar(12) declare @rgstring nvarchar(38) -- Owner qualified declare @procname nvarchar(270) /* ** To public. */ if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end set @tnstring = convert(nchar, @tablenick) set @rgstring = '''' + convert(nchar(36), @rowguid) + '''' create table #cont (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(249) NULL ,colv1 varbinary(2048) NULL) declare @selecttop nvarchar(20) if (@maxrows = 0) set @selecttop= 'select' else set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) execute ('insert into #cont ' + @selecttop + ' tablenick, rowguid, generation, lineage, colv from ' + @temp_cont + ' where tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + ' order by rowguid') if @@ERROR<>0 begin return (1) end select @procname = select_proc from sysmergearticles where nickname=@tablenick and pubid = @pubid exec @retcode = @procname @type =3 IF @@ERROR<>0 or @retcode<>0 begin RETURN (1) end drop table #cont return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumpartialchanges go grant exec on dbo.sp_MSenumpartialchanges to public raiserror('Creating procedure sp_MSinitdynamicsubscriber', 0,1) GO CREATE PROCEDURE sp_MSinitdynamicsubscriber (@maxrows int, @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL) as declare @retcode smallint declare @procname nvarchar(290) /* ** To public. */ if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end select @procname = view_sel_proc from sysmergearticles where pubid = @pubid and nickname = @tablenick exec @retcode = @procname @tablenick, @maxrows, @rowguid if @@ERROR<>0 or @retcode<>0 begin return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSinitdynamicsubscriber go grant exec on dbo.sp_MSinitdynamicsubscriber to public raiserror('Creating procedure sp_MSgetrowmetadata', 0,1) GO CREATE PROCEDURE sp_MSgetrowmetadata (@tablenick int, @rowguid uniqueidentifier, @generation int output, @type tinyint output, @lineage varbinary(255) output, @colv varbinary(2048) output, @pubid uniqueidentifier = NULL) as declare @retcode smallint declare @saverr int declare @rc int declare @procname nvarchar(270) /* ** To public. */ if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end set @type= 4 set @generation= 0 set @lineage= NULL set @colv= NULL select @procname= select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid -- check for row in base table exec @retcode= @procname @type=@type output, @rowguid=@rowguid if @@error <>0 or @retcode <> 0 begin return (1) end begin tran if (@type = 3) begin -- row is in base table; check whether it is in contents, too -- -- serializable makes sure row does not go from tombstone to contents between -- querying contents and tombstone, which would falsely result in type = missing select @type= 2, @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents with (serializable) where tablenick = @tablenick and rowguid = @rowguid end else begin -- row is not in base table; either it is in tombstone, or it is missing set @type= 0 select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid end commit tran return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetrowmetadata go grant exec on dbo.sp_MSgetrowmetadata to public go raiserror('Creating procedure sp_MSgetmetadatabatch', 0,1) GO CREATE PROCEDURE sp_MSgetmetadatabatch (@pubid uniqueidentifier, @tablenickarray varbinary(2000), @rowguidarray varbinary(8000) ) as declare @tablenick int declare @tablenicklast int declare @rowguid uniqueidentifier declare @generation int declare @type tinyint declare @lineage varbinary(255) declare @colv varbinary(2048) declare @retcode smallint declare @saverr int declare @tnlength int declare @tnoffset int declare @guidoffset int declare @procname nvarchar(270) -- create temp table for returning results declare @meta_batch TABLE (idx int identity, generation int, type tinyint, lineage varbinary(255), colv varbinary(2048)) /* ** To public. */ if (@tablenickarray is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@rowguidarray is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end set @tablenicklast = 0 -- initialize offsets and length for walking through arrays set @tnoffset = 1 set @guidoffset = 1 set @tnlength = datalength(@tablenickarray) -- walk through arrays and populate temp table while (@tnoffset < @tnlength) begin set @tablenick = substring(@tablenickarray, @tnoffset, 4) set @rowguid = substring(@rowguidarray, @guidoffset, 16) -- instead of calling sp_MSgetrowmetadata, look it up ourselves might be faster -- exec @rc = sp_MSgetrowmetadata @tablenick, @rowguid, @generation output, -- @type output, @lineage output, @colv output, @pubid if @tablenick <> @tablenicklast begin select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid=@pubid set @tablenicklast = @tablenick end set @type= 4 set @generation= 0 set @lineage= NULL set @colv= NULL -- check for row in base table exec @retcode= @procname @type=@type output, @rowguid=@rowguid if @@error <>0 or @retcode <> 0 begin return (1) end begin tran if (@type = 3) begin -- row is in base table; check whether it is in contents, too -- -- serializable makes sure row does not go from tombstone to contents between -- querying contents and tombstone, which would falsely result in type = missing select @type= 2, @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents with (serializable) where tablenick = @tablenick and rowguid = @rowguid end else begin -- row is not in base table; either it is in tombstone, or it is missing set @type= 0 select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid end commit tran -- insert values into temp table insert into @meta_batch (generation, type, lineage, colv) values (@generation, @type, @lineage, @colv) -- bump up offsets for next time through loop set @tnoffset = @tnoffset + 4 set @guidoffset = @guidoffset + 16 end -- select out our result set select generation, type, lineage, colv from @meta_batch order by idx return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetmetadatabatch go grant exec on dbo.sp_MSgetmetadatabatch to public go raiserror('Creating procedure sp_MSsetrowmetadata', 0,1) GO CREATE PROCEDURE sp_MSsetrowmetadata (@tablenick int, @rowguid uniqueidentifier, @generation int, @lineage varbinary(255), @colv varbinary(2048), @type tinyint, @tombstone_rows_deleted int = NULL OUTPUT) as declare @reason nvarchar(255), @retcode int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (1) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end if (@generation is null) begin RAISERROR(14043, 16, -1, '@generation') return (1) end if (@lineage is null) begin RAISERROR(14043, 16, -1, '@lineage') return (1) end if (@type=1 or @type=5 or @type=6) begin if @type = 5 begin set @reason = formatmessage(20563) -- Out of partial range set @lineage = 0x00 end else if @type = 6 begin set @reason = formatmessage(20564) -- Deleted by system (e.g., to cope with dup key) end else begin set @reason = formatmessage(20562) -- Deleted by user end -- update or insert dbo.MSmerge_tombstone update dbo.MSmerge_tombstone set generation = @generation, lineage = @lineage, type = @type, reason = @reason where tablenick = @tablenick and rowguid = @rowguid if (@@rowcount = 0) begin insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, generation, lineage, reason) values (@rowguid, @tablenick, @type, @generation, @lineage, @reason) delete from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid end end else begin if not exists (select rowguid from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid) begin insert into dbo.MSmerge_contents (rowguid, tablenick, generation, lineage, colv1) values (@rowguid, @tablenick, @generation, @lineage, @colv) delete from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid select @tombstone_rows_deleted = @@rowcount end else begin -- update or insert to MSmerge_contents -- The following updates the generation to passed in value only if the -- filter columns have not changed. -- If filter values have changed, update the generation to gen_cur for article - -- This will allow a subsequent download to cleanup rows that don't belong at subscriber update dbo.MSmerge_contents set generation = case when (isnull(partchangegen, -1) <> sma.gen_cur and isnull(joinchangegen, -1) <> sma.gen_cur) then @generation else sma.gen_cur end, lineage = @lineage, colv1 = case when datalength(@colv) < datalength(colv1) then colv1 else @colv end from dbo.MSmerge_contents mc, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as sma where tablenick = @tablenick and rowguid = @rowguid and mc.tablenick = sma.nickname if (@@rowcount = 0) begin insert into dbo.MSmerge_contents (rowguid, tablenick, generation, lineage, colv1) values (@rowguid, @tablenick, @generation, @lineage, @colv) delete from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid select @tombstone_rows_deleted = @@rowcount end end end IF @@ERROR<>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetrowmetadata go grant exec on dbo.sp_MSsetrowmetadata to public go raiserror('Creating procedure sp_MSinsertgenhistory', 0,1) GO CREATE PROCEDURE sp_MSinsertgenhistory (@guidsrc uniqueidentifier, @gen int output, @pubid uniqueidentifier, @pubid_ins uniqueidentifier = NULL, @nicknames varbinary(1000) = 0x0, @artnick int = NULL ) as /* ** Check to see if current publication has permission */ declare @retcode int, @mynickname int, @newnicks varbinary(1000), @dt datetime if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end if (@guidsrc is null) begin RAISERROR(14043, 16, -1, '@guidsrc') return (1) end -- having the login time in dbo.MSmerge_genhistory allows to associate the row with the merge process that inserted the row select @dt= login_time from master..sysprocesses where spid = @@spid -- Check for older in process generation select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @guidsrc if @gen is not null begin -- this generation was interrupted at a previous merge -- next statement makes sure that gen does no longer look interrupted to another process update dbo.MSmerge_genhistory set coldate= @dt where guidsrc = @guidsrc -- if @@rowcount = 0, another process removed the interrupted gen just before the previous update statement if @@rowcount > 0 begin return (0) end end exec dbo.sp_MSgetreplnick @nickname = @mynickname out if @@ERROR<>0 return (1) -- Append guard byte if it is needed if @nicknames = 0x0 and (@mynickname % 256 = 0) set @newnicks = convert(binary(4), @mynickname) + 0x01 else set @newnicks = convert(binary(4), @mynickname) + @nicknames begin tran save tran sp_MSinsertgenhistory select @gen = COALESCE(1 + max(generation), 1) from dbo.MSmerge_genhistory (updlock) if (@gen is NULL) select @gen = 1 insert into dbo.MSmerge_genhistory (guidsrc, pubid, guidlocal, generation, art_nick, nicknames, coldate) values (@guidsrc, @pubid_ins, '00000000-0000-0000-0000-000000000000', @gen, @artnick, @newnicks, @dt) if @@error<>0 goto Failure commit tran return (0) Failure: rollback tran sp_MSinsertgenhistory commit tran RAISERROR(15001, 16, -1, 'MSmerge_genhistory') return (1) go exec dbo.sp_MS_marksystemobject sp_MSinsertgenhistory go grant exec on dbo.sp_MSinsertgenhistory to public raiserror('Creating procedure sp_MSupdategenhistory', 0,1) GO CREATE PROCEDURE sp_MSupdategenhistory (@guidsrc uniqueidentifier, @pubid uniqueidentifier, @gen int, @art_nick int = NULL) as declare @guidlocal uniqueidentifier /* ** Check to see if current publication has permission */ declare @retcode int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end if (@guidsrc is null) begin RAISERROR(14043, 16, -1, '@guidsrc') return (1) end if @art_nick = 0 set @art_nick = NULL set @guidlocal = newid() begin tran save tran sp_MSupdategenhistory if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen) begin create table #gentable (generation int) insert into #gentable select generation from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen update mc set mc.generation= @gen from dbo.MSmerge_contents as mc inner join #gentable as g on (mc.generation=g.generation) if @@ERROR <> 0 goto FAILURE update mt set mt.generation= @gen from dbo.MSmerge_tombstone as mt inner join #gentable as g on (mt.generation=g.generation) if @@ERROR <> 0 goto FAILURE declare @cmd nvarchar(200) declare @bi_objid int set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @art_nick) if @bi_objid is not null begin set @cmd= 'update bi set bi.generation= @gen from dbo.' + object_name(@bi_objid) + ' as bi inner join #gentable as g on (bi.generation = g.generation)' exec dbo.sp_executesql @cmd, N'@gen int', @gen= @gen if @@ERROR <> 0 goto FAILURE end delete from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen if @@ERROR <> 0 goto FAILURE drop table #gentable end if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc) update dbo.MSmerge_genhistory set guidlocal= @guidlocal, art_nick = case when isnull(@art_nick,0) <> 0 then @art_nick else art_nick end, coldate= getdate() where guidsrc = @guidsrc else begin declare @mynickname int declare @nickbin varbinary(255) exec dbo.sp_MSgetreplnick @nickname = @mynickname out if @@ERROR<>0 goto FAILURE -- Append guard byte if it is needed if @mynickname % 256 = 0 set @nickbin = convert(binary(4), @mynickname) + 0x01 else set @nickbin = convert(binary(4), @mynickname) insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values (@guidsrc, @guidlocal, @gen, @art_nick, @nickbin , getdate()) end commit -- Now that we have closed a generation that was open, we might be ready to -- cleanup metadata or something like that. exec @retcode = sp_MSquiescecheck return (0) FAILURE: rollback tran sp_MSupdategenhistory commit tran return(1) go exec dbo.sp_MS_marksystemobject sp_MSupdategenhistory go grant exec on dbo.sp_MSupdategenhistory to public go raiserror('Creating procedure sp_MSlocalizeinterruptedgenerations', 0,1) GO CREATE PROCEDURE sp_MSlocalizeinterruptedgenerations @localize_zeroartnick_generations bit=0 -- this proc loops over interrupted generations -- and transfers those changes that arrived before the interrupt to a new local generation as begin declare @new_guidsrc uniqueidentifier if @localize_zeroartnick_generations = 0 update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate() where guidlocal = '00000000-0000-0000-0000-000000000000' -- incomplete gen and generation not in (select gen_cur from sysmergearticles) -- not a local incomplete gen and coldate not in (select login_time from master..sysprocesses) -- not a gen that currently receives replica updates from another db and isnull(art_nick, 0) <> 0 -- we don't localize generations with art_nick = 0 or NULL. else update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate() where guidlocal = '00000000-0000-0000-0000-000000000000' -- incomplete gen and generation not in (select gen_cur from sysmergearticles) -- not a local incomplete gen and coldate not in (select login_time from master..sysprocesses) -- not a gen that currently receives replica updates from another db if @@error <> 0 return 1 else return 0 end go exec dbo.sp_MS_marksystemobject sp_MSlocalizeinterruptedgenerations go grant exec on dbo.sp_MSlocalizeinterruptedgenerations to public go raiserror('Creating procedure sp_MSenumschemachange', 0,1) GO CREATE PROCEDURE sp_MSenumschemachange( @pubid uniqueidentifier, @schemaversion int, @compatibility_level int = 7000000, @AlterTableOnly bit = 0 ) as set nocount on declare @retcode int select @retcode = 0 -- Delegate call to the appropriate sp that -- handles the given compatibility-level if @compatibility_level > 7000200 --this value standing for 70SP2 begin exec @retcode = sp_MSenumschemachange_80 @pubid = @pubid, @AlterTableOnly = @AlterTableOnly, @schemaversion = @schemaversion end else begin exec @retcode = sp_MSenumschemachange_70 @pubid = @pubid, @AlterTableOnly = @AlterTableOnly, @schemaversion = @schemaversion, @compatibility_level=@compatibility_level end return @retcode go exec dbo.sp_MS_marksystemobject sp_MSenumschemachange go grant exec on dbo.sp_MSenumschemachange to public go raiserror('Creating procedure sp_MSenumschemachange_70', 0,1) GO CREATE PROCEDURE sp_MSenumschemachange_70( @pubid uniqueidentifier, @AlterTableOnly int, @schemaversion int, @compatibility_level int ) as /* ** To public */ if (@schemaversion is null) begin RAISERROR(14043, 16, -1, '@schemaversion') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end create table #temp_table_sp_MSenumschemachange_70 (pubid uniqueidentifier NULL, artid uniqueidentifier NULL, schemaversion int NULL, schemaguid uniqueidentifier NULL, schematype int NULL, schematext nvarchar(2000) collate database_default null) /* handle the case of reinitall */ if exists (select pubid from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 12) select @schemaversion = 0 /* ** For 70 RTM and SP1, we want to filter out type 9 (retention propagation) and 16 (metadata cleanup) ** and 5 ( last rec generation ) and 6 (last sent generation) ** which they do not support. SP2 subscriber will be passing in 7000200, which gets understands ** these schema types */ insert into #temp_table_sp_MSenumschemachange_70 select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and ((@compatibility_level=7000000 and schematype in (-1, 1, 2, 3, 4, 7, 8, 10, 15, 20)) or (@compatibility_level=7000200 and ((schematype in (-1, 1, 2, 3, 4, 7, 8, 9, 10, 12, 15, 16, 20,66)) or schematype in (5, 6) and @schemaversion = 0))) order by schemaversion /* for SP2 downlevel subscribers who do not understand type 12 (reinit-all) at provider level, but do ** at reconcile level, change it to type = 7 (directory) to be no-op. */ if @compatibility_level=7000200 update #temp_table_sp_MSenumschemachange_70 set schematype=7 where schematype = 12 select * from #temp_table_sp_MSenumschemachange_70 drop table #temp_table_sp_MSenumschemachange_70 return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumschemachange_70 go grant exec on dbo.sp_MSenumschemachange_70 to public go raiserror('Creating procedure sp_MSenumschemachange_80', 0,1) GO CREATE PROCEDURE sp_MSenumschemachange_80( @pubid uniqueidentifier, @AlterTableOnly int, @schemaversion int ) as set nocount on /* ** To public */ declare @alter_table_type int declare @reinit_all_type int declare @reinit_all_upload_type int declare @schemaversion_of_snapshottrailer int select @reinit_all_type = 12 select @alter_table_type = 11 select @reinit_all_upload_type = 14 if (@schemaversion is null) begin RAISERROR(14043, 16, -1, '@schemaversion') return (1) end -- @schema_needed = 0 - only send back reinitall command, if any -- @schema_needed = 1 - normal enumeration -- @schema_needed = 2 - only send back alter-table command, if any -- @schema_needed = 3 - only send back reinitall-with-upload command, if any. if (@AlterTableOnly = 1) begin select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where pubid=@pubid and schemaversion > @schemaversion and schematype = @alter_table_type return (0) end if exists (select * from sysmergeschemachange where pubid=@pubid and ((schemaversion > @schemaversion and schematype = @reinit_all_type) or (schemaversion > @schemaversion and schematype = @reinit_all_upload_type))) begin select @schemaversion=0 end -- If subscriber missed a preparecleanup and a completecleanup they must be reinitialized if exists (select * from sysmergeschemachange where pubid=@pubid and schemaversion > @schemaversion and schematype = 17) and exists (select * from sysmergeschemachange where pubid=@pubid and schemaversion > @schemaversion and schematype = 19) begin set @schemaversion=0 end if (@schemaversion > 0) begin -- Subscriber has already received the snapshot so filter out -- the pre/post-snapshot commands. -- Also filter out the schemtypes for the setlastsentgen (5) and setlastrecgen (6) -- This ensures that the subscriber does not apply these schema changes when -- it applies incremental schema - ie the perf optimization that is implemented -- by setting last sent/rec generation should be done only for brand new subscriptions. select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 25, 5, 6) order by schemaversion end Else begin -- Subscriber requires a snapshot, so carefully sequence the -- pre/post-snapshot commands around the snapshot boundary create table #schemachanges ( pubid uniqueidentifier NOT NULL, artid uniqueidentifier NULL, schemaversion int NOT NULL, schemaguid uniqueidentifier NOT NULL, schematype int NOT NULL, schematext nvarchar(2000) collate database_default not null, seqno int identity NOT NULL ) truncate table #schemachanges -- Insert snapshot header -- Header begins insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 50 -- Header content insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype in (25, 53, 54, 55, 56, 57, 58) order by schemaversion -- Header ends insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 51 -- End of snapshot header -- Insert pre command insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 40 -- Exclude pre-post, but include snapshot only commands insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype in (2, 3, 4, 20, 7, 60, 61, 62, 63, 64) order by schemaversion -- Dynamic BCP commands insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype in (131, 132) order by schemaversion -- DRI/Trg/XPROP insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype in (10, 15, 65) order by schemaversion -- Insert post command insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 45 -- Insert snapshot trailer insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 52 -- Insert other schema changes insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion and schematype not in (2, 3, 4, 10, 15, 20, 7, 40, 45, 60) and schematype not in (61, 62, 63, 64, 65) and schematype not in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58) and schematype not in (131, 132) -- dynamic bcp commands and schematype not in (46, 11, 13) -- The on-demand script and schema replication commands should not be enumerated unless -- we have made sure that it was posted after the current snapshot. and pubid = @pubid order by schemaversion -- get the schemaversion of the snapshot trailer row select @schemaversion_of_snapshottrailer = schemaversion from sysmergeschemachange where schemaversion > @schemaversion and pubid = @pubid and schematype = 52 if (@schemaversion_of_snapshottrailer is not null) begin -- insert schema changes for on-demand script and schema replication commands which have schemaversion greater than -- schemaversion of the snapshot trailer row insert into #schemachanges select pubid, artid, schemaversion, schemaguid, schematype, schematext from sysmergeschemachange where schemaversion > @schemaversion_of_snapshottrailer and schematype in (46, 11, 13) -- The on-demand script and schema replication commands only. and pubid = @pubid order by schemaversion end declare @endofsnapshot int select @endofsnapshot = min(seqno) from #schemachanges where schematype = 52 delete #schemachanges where seqno > @endofsnapshot and (schematype in (2, 3, 4, 10, 15, 20, 7, 40, 45, 65) or schematype in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58)) --only list the last reinitall command delete schemachanges1 from #schemachanges schemachanges1,#schemachanges schemachanges2 where schemachanges1.pubid=@pubid and schemachanges1.schematype=12 and schemachanges2.pubid=@pubid and schemachanges2.schematype=12 and schemachanges1.schemaversion<schemachanges2.schemaversion --only list the last reinitall-with-upload command delete schemachanges1 from #schemachanges schemachanges1, #schemachanges schemachanges2 where schemachanges1.pubid=@pubid and schemachanges1.schematype=14 and schemachanges2.pubid=@pubid and schemachanges2.schematype=14 and schemachanges1.schemaversion<schemachanges2.schemaversion select pubid, artid, schemaversion, schemaguid, schematype, schematext from #schemachanges order by seqno drop table #schemachanges end return (0) go exec dbo.sp_MS_marksystemobject sp_MSenumschemachange_80 go grant exec on dbo.sp_MSenumschemachange_80 to public go raiserror('Creating procedure sp_MSupdateschemachange', 0,1) GO CREATE PROCEDURE sp_MSupdateschemachange( @pubid uniqueidentifier, @artid uniqueidentifier = NULL, /* Can be NULL for directory commands */ @schemaversion int, @schemaguid uniqueidentifier, @schematype int, @schematext nvarchar(2000) ) as /* ** Check to see if current publication has permission */ declare @retcode int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end /* Parameter validation */ if (@schemaversion is null) begin RAISERROR(14043, 16, -1, '@schemaversion') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end update sysmergeschemachange set schematext = @schematext, schematype = @schematype where pubid = @pubid and schemaversion = @schemaversion if @@error <> 0 begin RAISERROR(20054 , 16, -1) return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSupdateschemachange go grant exec on dbo.sp_MSupdateschemachange to public go raiserror('Creating procedure sp_MSremove_mergereplcommand', 0,1) GO /* Remove an article command of a specific tyep from sysmergeschemachange- Used by snapshot */ CREATE PROCEDURE sp_MSremove_mergereplcommand ( @publication sysname, @article sysname, @schematype int ) AS set nocount on declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @retcode int exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) if @publication IS NULL begin raiserror (14003, 16, -1) return (1) end if @article IS NULL begin raiserror (20045, 16, -1) return (1) end select @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() select @artid = artid FROM sysmergearticles WHERE name = @article and pubid=@pubid delete sysmergeschemachange where pubid = @pubid and artid = @artid and schematype = @schematype GO exec dbo.sp_MS_marksystemobject sp_MSremove_mergereplcommand go grant exec on dbo.sp_MSremove_mergereplcommand to public go raiserror('Creating procedure sp_MSadd_mergereplcommand', 0,1) GO /* Add the replication command to the database - Used by snapshot */ CREATE PROCEDURE sp_MSadd_mergereplcommand ( @publication sysname, @article sysname = NULL, @schematype int, @schematext nvarchar(2000) ) AS declare @schemaguid uniqueidentifier declare @schemaversion int declare @retcode int declare @pubid uniqueidentifier declare @artid uniqueidentifier /* ** Publish permission check */ exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) if @publication IS NULL BEGIN RAISERROR (14003, 16, -1) RETURN (1) END select @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() select @artid = artid FROM sysmergeextendedarticlesview WHERE name = @article and pubid = @pubid /* ** For certain system tables that are bcped out such as MSmerge_contents ** and dbo.MSmerge_tombstone use the article name as sysobjects.name and get ** sysobjects.id as the artid */ if (@artid IS NULL) AND (@schematype <> 7) begin declare @binguid binary(16) set @binguid = OBJECT_ID(@article) set @artid = convert(uniqueidentifier, @binguid) end if exists (select * from sysmergeschemachange where pubid = @pubid AND artid = @artid AND (schematype = @schematype or @schematype in (3,4) and schematype in (3,4)) ) begin /* Select the existing schema guid */ select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange where pubid = @pubid AND artid = @artid AND (schematype = @schematype or @schematype in (3,4) and schematype in (3,4)) /* For directory commands, delete the previous directory before the update */ if (@schematype = 7) begin /* Remove the alternate directory command, pre/post ** commands, and snapshot header commands from the ** sysmergeschemachange table */ delete sysmergeschemachange where pubid = @pubid AND schematype in (25, 40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58) end /* ** Update the schema change version */ exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 begin RAISERROR(20054 , 16, -1) return (1) end end else begin /* Insert the schema change */ select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange /* update the schema version and schemaguid in MSmerge_replinfo */ declare @srvid int declare @repid uniqueidentifier declare @MSmerge_replinfoschemaversion int declare @MSmerge_replinfoschemaguid uniqueidentifier select @srvid = 0 select @repid = repid from MSmerge_replinfo where repid in (select subid from sysmergesubscriptions where srvid = @srvid and db_name = DB_NAME() and pubid = @pubid) select @MSmerge_replinfoschemaversion = schemaversion, @MSmerge_replinfoschemaguid = schemaguid from dbo.MSmerge_replinfo where repid = @repid /* ** If the schema version matches the MSmerge_replinfo.schemaversion and the schema_type = 52 (snapshot trailer), ** then reuse the MSmerge_replinfo.schema_guid. Otherwise generate a new guid. */ if @MSmerge_replinfoschemaversion is NOT NULL and @schemaversion = @MSmerge_replinfoschemaversion and @schematype = 52 begin set @schemaguid = @MSmerge_replinfoschemaguid end else begin set @schemaguid = newid() end exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 begin RAISERROR(20054 , 16, -1) return (1) end end if (@schematype = 7) begin update sysmergesubscriptions set last_validated=getdate() where pubid=@pubid and subid=@pubid IF @@ERROR <> 0 begin RAISERROR(20054 , 16, -1) RETURN (1) end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSadd_mergereplcommand go grant exec on dbo.sp_MSadd_mergereplcommand to public go raiserror('Creating procedure sp_MSsetreplicainfo', 0,1) GO CREATE PROCEDURE sp_MSsetreplicainfo (@publisher sysname, @publisher_db sysname, @publication sysname, @datasource_type int = 0, /* 0 = SQL Server, 1 = DSN, 2 = Jet */ @server_name sysname = NULL, /* Server Name */ @db_name sysname = NULL, /* Database Name */ @datasource_path nvarchar(255) = NULL,/* Datasource path - JET MDB file path etc */ @nickname int = NULL, @schemaversion int = NULL, @subid uniqueidentifier = NULL) as declare @pubid uniqueidentifier declare @repid uniqueidentifier declare @srvid int declare @retcode int /* ** NOTE -- WORKAROUND ODBC PROBLEM */ select @publisher_db = RTRIM(@publisher_db) select @db_name = RTRIM(@db_name) /* ** Subscriber permission check */ if UPPER(@@servername) = UPPER(@publisher) and db_name() = @publisher_db begin if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @publication = @publication if @retcode<>0 or @@ERROR<>0 return (1) end end else begin exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) end /* ** Parameter Check: @publication. ** Make sure that the publication exists. */ if (@publication is null) begin RAISERROR(14043, 16, -1, '@publication') return (1) end if (@server_name is NULL) SET @server_name = @@SERVERNAME if (@db_name is NULL) set @db_name = db_name() SELECT @srvid = srvid FROM master..sysservers WHERE UPPER(srvname) = UPPER(@server_name) collate database_default IF @@ERROR <> 0 or @srvid IS NULL BEGIN RAISERROR(20021, 16, -1) RETURN (1) END select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db if (@pubid is null) begin RAISERROR (20026, 11, -1, @publication) return (1) end if (@datasource_type = 0) begin /* ** SQL Server */ SELECT @repid = subid FROM sysmergesubscriptions WHERE srvid = @srvid and db_name = @db_name and pubid = @pubid update sysmergesubscriptions set validation_level = 0, resync_gen=-1 where subid=@repid END ELSE IF (@datasource_type = 4) or (@datasource_type = 5) BEGIN /* ** Exchange or Oracle */ SELECT @repid = subid FROM sysmergesubscriptions WHERE srvid = @srvid and pubid = @pubid END ELSE BEGIN /* ** Jet */ SELECT @repid = subid FROM sysmergesubscriptions WHERE datasource_path = @datasource_path and pubid = @pubid END if @repid is NULL begin RAISERROR(20021, 16, -1) return (1) end update sysmergesubscriptions set status=1 where subid=@repid and (status=5 or status=0) if @schemaversion is not null begin update MSmerge_replinfo set schemaversion = @schemaversion where repid = @repid if @@error <> 0 begin RAISERROR(20054 , 16, -1) return (1) end end if @subid is not null and @subid <> @repid begin /* Fix the repid for pull subscribers before we copy around global replica rows */ update MSmerge_replinfo set repid = @subid where repid = @repid if @@error <> 0 begin RAISERROR(20054 , 16, -1) return (1) end update sysmergesubscriptions set subid = @subid where subid = @repid if @@error <> 0 begin RAISERROR(20054 , 16, -1) return (1) end end if @nickname IS NOT NULL begin /* If this nickname isn't already assigned, reset it */ if exists (select * from MSmerge_replinfo, sysmergesubscriptions where replnickname = @nickname and repid = subid and (srvid <> @srvid or db_name <> @db_name)) return (0) update MSmerge_replinfo set replnickname = @nickname where repid = @repid if @@error <> 0 begin RAISERROR(20054 , 16, -1) return (1) end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetreplicainfo go grant exec on dbo.sp_MSsetreplicainfo to public go raiserror('Creating procedure sp_MSsetreplicastatus', 0,1) GO CREATE PROCEDURE sp_MSsetreplicastatus (@subid uniqueidentifier, @status_value int ) AS /* ** Check to see if current publication has permission */ declare @retcode int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @repid = @subid if @retcode<>0 or @@ERROR<>0 return (1) end IF EXISTS (select subid from sysmergesubscriptions where subid=@subid) begin update sysmergesubscriptions set status = @status_value, resync_gen=-1 WHERE subid=@subid if @@ERROR<>0 return (1) -- Now that we have changed a replica's status we might be ready to -- cleanup metadata or something like that. exec @retcode = sp_MSquiescecheck if @@ERROR<>0 or @retcode <> 0 return (1) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetreplicastatus go grant exec on dbo.sp_MSsetreplicastatus to public go raiserror('Creating procedure sp_MScreateglobalreplica', 0,1) GO CREATE PROCEDURE sp_MScreateglobalreplica( @pubid uniqueidentifier = NULL, /* Publication ID */ @subid uniqueidentifier, /* Replica ID */ @partnerid uniqueidentifier, /* Partner's Replica ID */ @replica_server sysname, /* Replica server */ @replica_db sysname, /* Replica database */ @replica_priority real, /* Replica priority */ @subscriber_type tinyint = 0, /* Replica's subscriber type - global, hub */ @subscription_type int = 0, /* Replica's subscription type - push or pull */ @datasource_type int = 0, @datasource_path nvarchar(255) = NULL, @nickname int, /* Replica nickname */ @status int, /* Replica status */ @sync_type tinyint = 2, /* Replica sync type 1 = no sync, 2 = automatic */ @publication sysname = NULL, /* Replica publication */ @distributor sysname = NULL /* Replica's distributor */ ) AS SET NOCOUNT ON /* ** Declarations. */ DECLARE @replica_srvid int DECLARE @pubnickname int declare @retcode int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end /* ** NOTE */ select @replica_db = RTRIM(@replica_db) /* ** Initializations */ SELECT @replica_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@replica_server) collate database_default if @replica_srvid is NULL begin EXECUTE @retcode = dbo.sp_addserver @replica_server, @duplicate_ok='duplicate_ok' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14042, 16, -1) RETURN (1) END end SELECT @replica_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@replica_server) collate database_default BEGIN TRAN MScreateglobalreplica /* ** Populate the local copy of sysmergesubscriptions */ if exists (select * from sysmergesubscriptions where subid = @subid) begin update sysmergesubscriptions SET subid = @subid, datasource_type = @datasource_type, datasource_path = @datasource_path, srvid = @replica_srvid, db_name = @replica_db, pubid = @pubid, status = @status, subscriber_type = @subscriber_type, subscription_type = @subscription_type, priority = @replica_priority, sync_type = @sync_type, login_name = suser_sname(suser_sid()), subscriber_server = @replica_server, publication = @publication, distributor = @distributor where subid = @subid and pubid = @pubid IF @@ERROR <> 0 goto FAILURE end else begin /* ** If attempting to tell the current replica about another replica whose pubid IS NULL ** ignore the insert because current replica has more current info. */ if exists (select * from sysmergesubscriptions where subid = @subid and @pubid IS NULL) goto SUCCESS IF EXISTS (SELECT * FROM sysmergesubscriptions WHERE srvid = @replica_srvid AND db_name = @replica_db and pubid = @pubid) goto SUCCESS if not exists (select * from sysmergesubscriptions where subid=@subid) begin insert sysmergesubscriptions(subid, partnerid, datasource_type, datasource_path, srvid, db_name, pubid, status, subscriber_type, subscription_type, priority, sync_type, login_name, subscriber_server, publication, distributor) values (@subid, @partnerid, @datasource_type, @datasource_path, @replica_srvid, @replica_db, @pubid, @status, @subscriber_type, @subscription_type, @replica_priority, @sync_type, suser_sname(suser_sid()), @replica_server, @publication, @distributor) IF @@ERROR <> 0 goto FAILURE end end if not exists (select * from MSmerge_replinfo where repid = @subid ) begin INSERT INTO MSmerge_replinfo(repid, replnickname) values (@subid, @nickname) IF @@ERROR <> 0 goto FAILURE end SUCCESS: COMMIT TRAN RETURN 0 FAILURE: /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT = 1 ROLLBACK TRANSACTION MScreateglobalreplica else COMMIT TRANSACTION RAISERROR (14057, 16, -1) RETURN 1 go exec dbo.sp_MS_marksystemobject sp_MScreateglobalreplica go grant exec on dbo.sp_MScreateglobalreplica to public go raiserror('Creating procedure sp_MSsetconflictscript', 0,1) GO /* Add the conflict script pointer to sysmergearticles - Used by snapshot */ CREATE PROCEDURE sp_MSsetconflictscript ( @publication sysname, @article sysname, @conflict_script nvarchar(255), @login sysname =NULL, @password nvarchar(524) =NULL ) AS declare @artid uniqueidentifier declare @pubid uniqueidentifier /* ** Check for publish permission. */ declare @retcode int exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @artid = artid FROM sysmergearticles WHERE name = @article AND pubid = @pubid if @artid IS NULL BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END update sysmergearticles set conflict_script = @conflict_script where artid = @artid and pubid=@pubid if @@ERROR <> 0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetconflictscript go grant exec on dbo.sp_MSsetconflictscript to public go raiserror('Creating procedure sp_MSsetconflicttable', 0,1) GO /* Add the conflict table pointer to sysmergearticles - Used by reconciler */ CREATE PROCEDURE sp_MSsetconflicttable ( @article sysname, @conflict_table nvarchar(255), @publisher sysname = NULL, @publisher_db sysname = NULL, @publication sysname = NULL ) AS declare @artid uniqueidentifier declare @pubid uniqueidentifier declare @quoted_conflict_table nvarchar(270) declare @basetableid int --special case'd this out for backward compatibility with 7.0 subscribers. if @publisher is NULL and @publisher_db is NULL and @publication is NULL return (0) select @pubid=pubid from sysmergepublications where name=@publication and LOWER(publisher)=LOWER(@publisher) and publisher_db=@publisher_db select @artid = artid, @basetableid=objid FROM sysmergearticles WHERE name = @article and pubid=@pubid if @artid IS NULL BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END /* ** Check to see if current publication has permission */ declare @retcode int if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @artid = @artid if @retcode<>0 or @@ERROR<>0 return (1) end select @quoted_conflict_table = quotename(@conflict_table) exec @retcode = dbo.sp_MS_marksystemobject @quoted_conflict_table update sysmergearticles set conflict_table = @conflict_table where artid = @artid and pubid=@pubid if @@ERROR <> 0 return (1) declare @rgcol nvarchar(135) declare @indname nvarchar(131) declare @owner sysname declare @quotedname nvarchar(270) select @rgcol = QUOTENAME(name) from syscolumns where id = @basetableid and ColumnProperty(id, name, 'isrowguidcol') = 1 select @owner=user_name(uid) from sysobjects where name=@conflict_table select @indname = 'uc_' + @conflict_table if len(@indname) > 128 begin select @indname = substring(@indname,1,92) + convert(nvarchar(36), newid()) end set @indname = QUOTENAME(@indname) set @quotedname = QUOTENAME(@owner) + '.' + QUOTENAME(@conflict_table) --only create the conflict table index when needed. if not exists (select * from sysindexes where id = object_id(@quotedname) and keys is not null) begin exec ('Create unique clustered index ' + @indname + ' on ' + @quotedname + ' (' + @rgcol + ', origin_datasource)' ) if @@error <> 0 return (1) end /* Create the conflict insert proc only when necessary for performance reason */ if exists (select * from sysmergearticles where artid = @artid and pubid=@pubid and OBJECT_ID(ins_conflict_proc) is null) BEGIN exec dbo.sp_MSgetconflictinsertproc @pubid=@pubid, @artid = @artid, @output = 0 IF @@ERROR<> 0 OR @retcode <> 0 return (1) END return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetconflicttable go grant exec on dbo.sp_MSsetconflicttable to public go raiserror('Creating procedure sp_MSmakeconflictinsertproc', 0,1) GO create procedure sp_MSmakeconflictinsertproc (@tablename sysname, @ownername sysname, @procname sysname, @basetableid int, @pubid uniqueidentifier=NULL) as declare @arglist nvarchar(4000) declare @header nvarchar(4000) declare @qualname nvarchar(270) declare @argname nvarchar(270) declare @noset bit declare @wherepc nvarchar(255) declare @id int declare @sync_objid int declare @colname nvarchar(140) declare @typename sysname declare @colid smallint declare @status tinyint declare @len smallint declare @prec smallint declare @scale int declare @retcode smallint declare @sys_loop bit declare @old_colname nvarchar(140) declare @create_time_col nvarchar(8) declare @p_number_for_conflict_type nvarchar(270) set nocount on select @sys_loop = 0 set @create_time_col = NULL set @id = NULL /* ** To check if specified object exists in current database */ select @id = id, @ownername=user_name(uid) from sysobjects where name = @tablename if @id is NULL return (1) set @qualname = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename) -- create temp table to select the command text out of create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) select @header = 'Create procedure dbo.' + @procname + ' ( ' insert into #tempcmd (phase, cmdtext) values (0, @header) select @sync_objid = sync_objid from sysmergearticles where objid = @basetableid and (pubid = @pubid or @pubid is NULL) select @colid = min(colid) from syscolumns where id = @id and iscomputed <>1 and type_name(xtype) <> 'timestamp' and ((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR (name in (select name from syscolumns where id=@basetableid) and @sys_loop =0)) select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length, @prec = c.prec, @scale = c.scale from syscolumns c, systypes t where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype /* ** Get the column list from the conflict_table schema and filter it with table view for vertical partitioning */ Reverse_Order: while (@colname is not null) begin set @noset = 0 if exists (select * from syscolumns where name=@colname and id=@basetableid) and not exists (select * from syscolumns where name=@colname and id=@sync_objid) goto NEXT_COL if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes select @len = @len/2 exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@ERROR<>0 or @retcode<>0 return (1) select @argname = '@p' + rtrim(convert(nchar, @colid)) if LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS)='conflict_type' select @p_number_for_conflict_type=@argname -- based on colid, add text to appropriate pieces if (COLUMNPROPERTY( @basetableid, @colname, 'IsRowGuidCol') = 1) begin select @noset =1 set @wherepc = ' where rowguidcol = ' + @argname end else if (@colname = 'origin_datasource') begin select @wherepc =@wherepc + ' and origin_datasource = ' + @argname set @noset =1 end set @old_colname = @colname set @colname = QUOTENAME(@colname) if @arglist is null begin set @arglist = @argname + ' ' + @typename --give default value of NULL to new merge columns for backward compatibility concern insert into #tempcmd (phase, cmdtext) values (3, @colname) select @header = ') values (' insert into #tempcmd (phase, cmdtext) values (4, @header) insert into #tempcmd (phase, cmdtext) values (4, @argname) if @noset=0 insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname) end else begin if len(@arglist)>3700 begin insert into #tempcmd (phase, cmdtext) values (0, @arglist) select @arglist = ' ' end set @arglist = @arglist + ', ' + @argname + ' ' + @typename if @sys_loop = 1 and @old_colname not in ('origin_datasource','conflict_type','reason_code','reason_text', 'pubid') begin select @arglist=@arglist + ' = NULL' if @old_colname='MSrepl_create_time' select @create_time_col=@argname end insert into #tempcmd (phase, cmdtext) values (3, ',' + @colname) insert into #tempcmd (phase, cmdtext) values (4, ',' + @argname) if @noset =0 begin if exists (select * from #tempcmd where phase=1) insert into #tempcmd (phase, cmdtext) values (1, ',' + @colname + ' = ' + @argname) else insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname) end end NEXT_COL: select @colid = min(colid) from syscolumns where id = @id and colid>@colid and iscomputed <>1 and type_name(xtype) <> 'timestamp' and ((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR (name in (select name from syscolumns where id=@basetableid) and @sys_loop =0)) set @colname = NULL select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length, @prec = c.prec, @scale = c.scale from syscolumns c, systypes t where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype end if @sys_loop = 0 begin select @sys_loop = 1 select @colid = min(colid) from syscolumns where id = @id and iscomputed <>1 and type_name(xtype) <> 'timestamp' and ((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR (name in (select name from syscolumns where id=@basetableid) and @sys_loop =0)) select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length, @prec = c.prec, @scale = c.scale from syscolumns c, systypes t where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype goto Reverse_Order end -- now create the procedure select @procname = QUOTENAME(@procname) insert into #tempcmd (phase, cmdtext) values (0, @arglist) select @header = ') as' insert into #tempcmd (phase, cmdtext) values (0, @header) select @header = ' ' -- for ease of expansion here in case we add new merge columns in conflict tables. if @create_time_col is not NULL select @header = @header + ' select ' + @create_time_col + ' = getdate() ' select @header = @header + ' if exists (select * from ' + @qualname + ' ' + @wherepc + ') begin update ' + @qualname + ' set ' insert into #tempcmd (phase, cmdtext) values (0, @header) --see comment in sp_MSinsertdeleteconflict for this <5 or >4 checking. select @header = @wherepc + ' and (conflict_type<5 or ' + @p_number_for_conflict_type + ' >4) end else insert into ' + @qualname + ' (' insert into #tempcmd (phase, cmdtext) values (2, @header) insert into #tempcmd (phase, cmdtext) values (4, ')') -- Now we select out the command text pieces in proper order so that our caller, -- xp_execresultset will execute the command that creates the stored procedure. select cmdtext from #tempcmd order by phase, step drop table #tempcmd go exec dbo.sp_MS_marksystemobject sp_MSmakeconflictinsertproc go grant exec on dbo.sp_MSmakeconflictinsertproc to public go dump tran master with no_log go raiserror('Creating procedure sp_MSmaketempinsertproc', 0,1) GO -- Create temp sp, no security check needed create procedure sp_MSmaketempinsertproc (@tablename sysname, @procname sysname) as declare @arglist nvarchar(4000) declare @collist nvarchar(4000) declare @vallist nvarchar(4000) declare @argname sysname declare @setpc nvarchar(4000) declare @wherepc nvarchar(255) declare @qualname nvarchar(270) declare @id int set @qualname = 'tempdb..' + @tablename select @id = id from tempdb..sysobjects where name = @tablename declare @colname sysname declare @typename sysname declare @colid smallint declare @status tinyint declare @len smallint declare @prec int declare @scale int declare @retcode smallint set @wherepc = ' where rowguid = @p2 ' select @colid = 1 select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length, @prec = COLUMNPROPERTY(c.id, c.name, 'precision'), @scale = c.scale from tempdb..syscolumns c, systypes t where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype and c.iscomputed<>1 and type_name(c.xtype) <> 'timestamp' while (@colname is not null) begin if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes select @len = @len/2 exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@error<>0 or @retcode<>0 return (1) set @argname = '@p' + rtrim(convert(nchar, @colid)) if (@colid = 1) begin set @arglist = @argname + ' ' + @typename set @collist = @colname set @vallist = @argname set @setpc = @colname + ' = ' + @argname end else begin set @arglist = @arglist + ', ' + @argname + ' ' + @typename set @collist = @collist + ', ' + @colname set @vallist = @vallist + ', ' + @argname set @setpc = @setpc + ', ' + @colname + ' = ' + @argname end set @colid = @colid + 1 set @colname = NULL select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length, @prec = COLUMNPROPERTY(c.id, c.name, 'precision'), @scale = c.scale from tempdb..syscolumns c, systypes t where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype and c.iscomputed<>1 and type_name(c.xtype)<>'timestamp' end declare @qual_tablename nvarchar(140) declare @qual_procname nvarchar(140) select @qual_procname = QUOTENAME(@procname) select @qual_tablename = QUOTENAME(@tablename) -- now create the procedure execute ('Create procedure dbo.' + @qual_procname + ' ( ' + @arglist + ') as update ' + @qual_tablename + ' set ' + @setpc + @wherepc + ' if (@@rowcount = 0) insert into ' + @qual_tablename + ' (' + @collist + ') values (' + @vallist + ')') if @@ERROR<>0 begin return(1) end go exec dbo.sp_MS_marksystemobject sp_MSmaketempinsertproc go grant exec on dbo.sp_MSmaketempinsertproc to public go raiserror('Creating procedure sp_MSgetconflictinsertproc', 0,1) GO /* Add the conflict table pointer to sysmergearticles - Used by reconciler */ CREATE PROCEDURE sp_MSgetconflictinsertproc ( @artid uniqueidentifier, @pubid uniqueidentifier = NULl, @output int = 1 ) AS declare @conflict_table sysname declare @conflict_proc sysname declare @owner sysname declare @object sysname declare @retcode int declare @quoted_conflict_table nvarchar(270) declare @basetableid int declare @sync_objid int declare @command nvarchar(1000) declare @pubidstr nvarchar(40) declare @dbname sysname declare @conflict_table_id int -- PARSENAME VARS declare @UnqualName nvarchar(270) --rightmost name node declare @QualName1 nvarchar(270) declare @QualName2 nvarchar(270) -- END PARSENAME VARS declare @guidstr varchar(40) exec @retcode=sp_MSguidtostr @artid, @guidstr out if @retcode<>0 or @@ERROR<>0 return (1) /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @artid = @artid if @retcode<>0 or @@ERROR<>0 return (1) end /* ** @pubid is NULL - means that subscriber is 7.0, in which case there is no ** vertical partitioning allowed. getting the proc from any publication is fine. */ select @conflict_table = conflict_table, @conflict_proc = ins_conflict_proc, @basetableid = objid, @sync_objid = sync_objid from sysmergearticles where artid = @artid and (pubid=@pubid OR @pubid is NULL) if @@ERROR <> 0 return (1) select @conflict_table_id = object_id(@conflict_table) if @conflict_table_id is NULL --check if it is caused by different owner name select @conflict_table_id=id from sysobjects where name=@conflict_table select @quoted_conflict_table = QUOTENAME(@conflict_table) if ( @conflict_table_id is not null) begin select @UnqualName = PARSENAME(@conflict_table, 1) select @QualName1 = PARSENAME(@conflict_table, 2) if @UnqualName IS NULL return 1 -- fixup for variable length differences. remove when vars expanded -- to new SQL SERVER 7.0 lengths select @owner=user_name(uid) from sysobjects where id= @conflict_table_id if @owner is NULL begin select @owner = @QualName1 end select @object = @UnqualName end -- Create an index on the conflict table if it doesn't have one if ( @conflict_table_id is not null) and not exists (select * from sysindexes where id = @conflict_table_id and keys is not null) begin declare @rgcol nvarchar(135) declare @indname nvarchar(270) declare @quotedname nvarchar(270) select @rgcol = QUOTENAME(name) from syscolumns where id = @basetableid and ColumnProperty(id, name, 'isrowguidcol') = 1 select @indname = 'uc_' + @conflict_table if len(@indname) > 128 begin select @indname = substring(@indname,1,92) + convert(nvarchar(36), newid()) end set @indname = QUOTENAME(@indname) set @quotedname = QUOTENAME(@owner) + '.' + QUOTENAME(@conflict_table) exec ('Create unique clustered index ' + @indname + ' on ' + @quotedname + ' (' + @rgcol + ', origin_datasource)' ) if @@error <> 0 return (1) end if ((OBJECT_ID(@conflict_proc) is null) and (@conflict_table_id is not null)) begin -- first set up the procedure name variable exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out if @@ERROR <>0 OR @retcode <>0 return (1) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <>0 return (1) if @pubid is not NULL select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) else select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 32) exec @retcode=sp_MSuniqueobjectname @conflict_proc , @conflict_proc output if @@ERROR <> 0 OR @retcode <> 0 return(1) set @dbname = db_name() if @owner is NULL set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , NULL , ' + @conflict_proc + ' , ' + convert(nvarchar,@basetableid) else set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , ' + QUOTENAME(@owner) + ' , ' + @conflict_proc + ' , ' + convert(nvarchar,@basetableid) if @pubid is not NULL set @command = @command + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode<>0 begin return (1) end exec @retcode = dbo.sp_MS_marksystemobject @conflict_proc if @@ERROR<>0 or @retcode<>0 return (1) exec ('grant exec on ' + @conflict_proc + ' to public') if @@ERROR<>0 return (1) update sysmergearticles set ins_conflict_proc = @conflict_proc where artid = @artid and pubid=@pubid end if @output = 1 select @conflict_table, @conflict_proc if @@ERROR <> 0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetconflictinsertproc go grant exec on dbo.sp_MSgetconflictinsertproc to public go raiserror('Creating procedure sp_MSinsertdeleteconflict', 0,1) GO create PROCEDURE sp_MSinsertdeleteconflict( @tablenick int, @rowguid uniqueidentifier, @conflict_type int, @reason_code int, @reason_text nvarchar(720), @origin_datasource nvarchar(255), @pubid uniqueidentifier, @lineage varbinary(255) = NULL, @conflicts_logged INT = NULL OUTPUT) as declare @retcode int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (1) end /* Parameter validation */ if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (1) end if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end /* Don't insert a duplicate row */ if (not exists (select * from MSmerge_delete_conflicts where tablenick = @tablenick and rowguid = @rowguid and origin_datasource = @origin_datasource)) begin insert into MSmerge_delete_conflicts (tablenick, rowguid, conflict_type, reason_code, reason_text, origin_datasource, pubid) values (@tablenick, @rowguid, @conflict_type, @reason_code, @reason_text, @origin_datasource, @pubid) select @conflicts_logged = @@ROWCOUNT if @@ERROR <> 0 return (1) end /* Update the existing row - but do not use a generic message to update an error */ else if (exists (select * from MSmerge_delete_conflicts where tablenick = @tablenick and rowguid = @rowguid and origin_datasource = @origin_datasource and (conflict_type<5 or @conflict_type>4))) begin update MSmerge_delete_conflicts set conflict_type = @conflict_type, reason_code = @reason_code, reason_text = @reason_text where tablenick = @tablenick and rowguid = @rowguid and origin_datasource = @origin_datasource select @conflicts_logged = @@ROWCOUNT if @@ERROR <> 0 return (1) end /* If this is an error, add to MSmerge_errorlineage table */ if @conflict_type in (7, 8) and @lineage is not null exec @retcode = sp_MSinserterrorlineage @tablenick, @rowguid, @lineage if @retcode<>0 or @@ERROR<>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSinsertdeleteconflict go grant exec on dbo.sp_MSinsertdeleteconflict to public go raiserror('Creating procedure sp_MScheckmetadatamatch', 0,1) GO create procedure sp_MScheckmetadatamatch (@metatype tinyint, @rowguid uniqueidentifier, @tablenick int, @lineage varbinary(255), @match int output) as if (@metatype = 3) begin if not exists (select * from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid) select @match = 1 else select @match = count(*) from dbo.MSmerge_contents (updlock) where tablenick = @tablenick and rowguid = @rowguid and lineage = @lineage end else select @match = count(*) from dbo.MSmerge_contents (updlock) where tablenick = @tablenick and rowguid = @rowguid and lineage = @lineage return (0) go exec dbo.sp_MS_marksystemobject sp_MScheckmetadatamatch go grant exec on dbo.sp_MScheckmetadatamatch to public raiserror('Creating procedure sp_MSdelrow', 0,1) GO create PROCEDURE sp_MSdelrow (@rowguid uniqueidentifier, @tablenick int, @metadata_type tinyint, /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */ @lineage_old varbinary(255), @generation int, @lineage_new varbinary(255), @pubid uniqueidentifier = NULL, @check_permission int = 0) as set nocount on declare @match int declare @new_metatype tinyint declare @retcode smallint declare @errcode int declare @reason nvarchar(255) declare @procname sysname declare @objid int declare @permissions int /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (0) end /* Parameter validation */ if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (0) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (0) end if (@lineage_new is null) begin RAISERROR(14043, 16, -1, '@lineage_new') return (0) end if @check_permission =1 begin select @objid=objid, @permissions= check_permissions from sysmergearticles where nickname=@tablenick and (pubid is NULL or pubid=@pubid) if @objid is NULL return (0) exec @retcode = dbo.sp_MSreplcheck_permission @objid = @objid, @type = 3, @permissions = @permissions if @retcode<>0 or @@ERROR<>0 return (4) end -- Are we just changing the type of a tombstone? -- This routine is only called for Upload; won't be type 5 (remove from partial) unless -- subscriber has a user delete and found an existing metadata type of 5 here. -- In that case, set delete type to 1 and update generation, reason text too. if (@metadata_type = 5) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin set @reason = formatmessage (20562) -- User delete update dbo.MSmerge_tombstone set type = 1, reason = @reason, generation = @generation, lineage = @lineage_new where rowguid = @rowguid and tablenick = @tablenick return 1 end end -- Are we just changing the type of a tombstone? else if (@metadata_type = 6) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin set @reason = formatmessage (20564) -- System deleted update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason, generation = @generation, lineage = @lineage_new where rowguid = @rowguid and tablenick = @tablenick return 1 end end -- begin transaction and lock row that we plan to delete begin transaction save tran sp_MSdelrow select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid exec @retcode = @procname @type =8, @rowguid=@rowguid IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end if @metadata_type = 5 begin set @match = 1 set @new_metatype = 5 end else if @metadata_type = 6 begin set @match = 1 set @new_metatype = 6 end else begin exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end set @new_metatype = 1 end if (@match = 1) begin -- select_proc makes a delete with @type = 5, despite its name. declare @rowcount int, @error int exec @retcode = @procname @type =5, @rowguid=@rowguid select @error=@@error, @rowcount=@@rowcount if @error<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end if (@rowcount = 1) begin exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end end else begin set @errcode= 3 goto Failure end end else begin set @errcode= 2 goto Failure end commit tran return(1)-- in sp_MSdelrow, 1=okay Failure: rollback tran sp_MSdelrow commit tran return(@errcode) go exec dbo.sp_MS_marksystemobject sp_MSdelrow go grant exec on dbo.sp_MSdelrow to public go raiserror('Creating procedure sp_MSsetartprocs', 0,1) GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON GO -- Call by snapshot create procedure sp_MSsetartprocs (@publication sysname, @article sysname, @force_flag int = 0) as declare @ownername sysname declare @objectname sysname declare @guidstr nvarchar(40) declare @pubidstr nvarchar(40) declare @conflict_proc sysname declare @conflict_table sysname declare @snapshot_ready int declare @ins_procname sysname declare @sel_procname sysname declare @upd_procname sysname declare @view_selprocname nvarchar(290) declare @viewname sysname declare @artid uniqueidentifier declare @pubid uniqueidentifier declare @objid int declare @rgcol nvarchar(140) declare @sync_objid int declare @retcode smallint declare @dbname sysname declare @command nvarchar(1000) set nocount on /* ** Check to see if current publication has permission */ exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) -- figure out pubid and artid if @force_flag = 1 begin -- don't qualify that must be publisher when we are forcing remake at subscribers select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and pubid in (select pubid from sysmergearticles where name=@article) end else select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @conflict_table=NULL select @artid = artid, @objid = objid, @sync_objid = sync_objid, @conflict_table=conflict_table FROM sysmergearticles WHERE name = @article AND pubid = @pubid if @artid IS NULL BEGIN RAISERROR (20027, 16, -1, @article) RETURN (1) END /* Drop the article procs if they preexist */ exec @retcode = dbo.sp_MSdroparticleprocs @pubid, @artid if @@ERROR<>0 OR @retcode<>0 begin return (1) end -- get owner name, and table name select @objectname = name, @ownername = user_name(uid) from sysobjects where id = @objid -- make the insert and update proc names exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out if @@ERROR <>0 OR @retcode <>0 return (1) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <>0 return (1) select @ins_procname = 'sp_ins_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec dbo.sp_MSuniqueobjectname @ins_procname, @ins_procname output if @@ERROR <>0 OR @retcode <>0 return (1) select @upd_procname = 'sp_upd_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec dbo.sp_MSuniqueobjectname @upd_procname, @upd_procname output if @@ERROR <>0 OR @retcode <>0 return (1) select @sel_procname = 'sp_sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec dbo.sp_MSuniqueobjectname @sel_procname, @sel_procname output if @@ERROR <>0 OR @retcode <>0 return (1) set @view_selprocname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec @retcode = dbo.sp_MSuniqueobjectname @view_selprocname , @view_selprocname output if @retcode <> 0 or @@ERROR <> 0 return (1) -- create the procs set @dbname = db_name() set @command = 'sp_MSmakeinsertproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @ins_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode<>0 begin return (1) end exec @retcode = dbo.sp_MS_marksystemobject @ins_procname if @@ERROR<>0 or @retcode<>0 return (1) exec ('grant exec on ' + @ins_procname + ' to public') set @command = 'sp_MSmakeupdateproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @upd_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode<>0 begin return (1) end exec @retcode = dbo.sp_MS_marksystemobject @upd_procname if @@ERROR<>0 or @retcode<>0 return (1) exec ('grant exec on ' + @upd_procname + ' to public') if @@ERROR<>0 return (1) set @command= 'SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON' exec (@command) if @@ERROR<>0 return (1) set @command = 'sp_MSmakeselectproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername)+ ' , ' + @sel_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 or @retcode<>0 begin return (1) end exec @retcode = dbo.sp_MS_marksystemobject @sel_procname if @@ERROR<>0 or @retcode<>0 return (1) exec ('grant exec on ' + @sel_procname + ' to public') if @@ERROR<>0 return (1) if @sync_objid <> 0 begin select @ownername = user_name(uid), @viewname = name from sysobjects where id = @sync_objid select @rgcol = QUOTENAME(name) from syscolumns where id = @objid and ColumnProperty(id, name, 'isrowguidcol') = 1 exec @retcode=dbo.sp_MSmakeviewproc @viewname, @ownername, @view_selprocname, @rgcol, @objid if @@ERROR<>0 or @retcode<>0 return (1) end else set @view_selprocname = '' --to be consistent with upgrade code by checking snapshot_ready>0 if @snapshot_ready>0 and @conflict_table is not NULl begin exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out if @@ERROR <>0 OR @retcode <>0 return (1) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <>0 return (1) select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16) exec @retcode=sp_MSuniqueobjectname @conflict_proc , @conflict_proc output if @@ERROR <> 0 OR @retcode <> 0 return(1) set @dbname = db_name() set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , ' + QUOTENAME(@ownername) + ' , ' + @conflict_proc + ' , ' + convert(nvarchar,@objid) set @command = @command + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode<>0 begin return (1) end exec @retcode = dbo.sp_MS_marksystemobject @conflict_proc if @@ERROR<>0 or @retcode<>0 return (1) exec ('grant exec on ' + @conflict_proc + ' to public') if @@ERROR<>0 return (1) update sysmergearticles set ins_conflict_proc = @conflict_proc where artid = @artid and pubid=@pubid end -- update articles to set the names update sysmergearticles set insert_proc = @ins_procname, update_proc = @upd_procname , select_proc = @sel_procname, view_sel_proc = @view_selprocname where artid = @artid and pubid = @pubid IF @@ERROR<>0 return (1) return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetartprocs go grant exec on dbo.sp_MSsetartprocs to public go SET ANSI_NULLS OFF GO raiserror('Creating procedure sp_MSmakesystableviews', 0,1) GO -- Used by snapshot create procedure sp_MSmakesystableviews ( @publication sysname, @dynamic_snapshot_views_table_name sysname = null ) AS declare @guidstr nvarchar(40) declare @pubid uniqueidentifier declare @contentsview sysname declare @tombstoneview sysname declare @genhistoryview sysname declare @filtersview sysname declare @piece nvarchar(4000) declare @retcode smallint declare @dbname sysname declare @art_count int declare @skip_ctsv int declare @command nvarchar(500) declare @dynamic_filters bit declare @view_creation_command nvarchar(4000) declare @newid uniqueidentifier /* ** Check to see if current publication has permission */ exec @retcode=sp_MSreplcheck_publish if @retcode<>0 or @@ERROR<>0 return (1) set @skip_ctsv = 0 select @pubid = pubid, @dynamic_filters = dynamic_filters from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() if @pubid is null BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END select @art_count=count(*) from sysmergearticles where pubid=@pubid if @art_count > 253 set @skip_ctsv=1 select @newid = newid() create table #temp_table_for_systable_view(contentsview sysname, tombstoneview sysname NULL, genhistoryview sysname NULL, filtersview sysname NULL) exec @retcode = dbo.sp_MSguidtostr @newid, @guidstr out if @@ERROR<>0 OR @retcode<>0 return (1) select @contentsview = 'cont' + @guidstr select @tombstoneview = 'ts' + @guidstr select @genhistoryview = 'gh' + @guidstr select @filtersview = 'filt' + @guidstr set @guidstr = '''' + convert(nchar(36), @pubid) + '''' exec @retcode = dbo.sp_MSuniqueobjectname @tombstoneview, @tombstoneview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @contentsview, @contentsview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @genhistoryview, @genhistoryview out if @@ERROR<>0 OR @retcode<>0 return (1) exec @retcode = dbo.sp_MSuniqueobjectname @filtersview, @filtersview out if @@ERROR<>0 OR @retcode<>0 return (1) insert #temp_table_for_systable_view values(@contentsview,@tombstoneview,@genhistoryview,@filtersview) if @skip_ctsv = 0 begin /* generate view for MSmerge_contents qualified by the pubid */ /* For dynamically filtered publication, security check is performed in the sync view of the base table */ set @command = 'sp_MSmakectsview ' + QUOTENAME(@publication) + ' , ' + @contentsview + ' , ' + COALESCE(QUOTENAME(@dynamic_snapshot_views_table_name) collate database_default, N'null' collate database_default) set @dbname = db_name() exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode <>0 return (1) end else begin exec('create view ' + @contentsview + ' as select * from MSmerge_contents where 1 = 2') if @@ERROR<>0 return (1) end /* ** generate the view for dbo.MSmerge_tombstone. In SP2 and Shiloh, the change was made to make the view ** return 0 rows since it is unnecessary and expensive to propagate the tombstones. ** In order to leave all the other moving parts unchanged, we decided to let the view ** return 0 rows. */ select @view_creation_command = 'create view ' + @tombstoneview + ' as select * from dbo.MSmerge_tombstone where 1= 2 and tablenick in (select nickname from sysmergearticles where pubid = ' + @guidstr + ')' if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end select @view_creation_command = 'create view ' + @genhistoryview + '(guidsrc, guidlocal, pubid, generation, art_nick, nicknames, coldate) as select DISTINCT guidsrc, guidlocal, CONVERT(uniqueidentifier, ' + @guidstr + '), generation, art_nick, nicknames, coldate from dbo.MSmerge_genhistory where guidlocal <> ''00000000-0000-0000-0000-000000000000'' and (art_nick = 0 or art_nick is NULL or art_nick in (select nickname from sysmergearticles where pubid = ' + @guidstr + ')) ' if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end select @view_creation_command = 'create view ' + @filtersview + ' as select * from sysmergesubsetfilters where pubid = ' + @guidstr if @dynamic_filters = 1 begin select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))' end exec (@view_creation_command) if @@ERROR <>0 begin return (1) end if @dynamic_filters = 1 begin exec ('grant select on ' + @contentsview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @tombstoneview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @genhistoryview + ' to public') if @@error<>0 return(1) exec ('grant select on ' + @filtersview + ' to public') if @@error<>0 return(1) end set nocount on /* we only generate per-article contents view for static publications */ if @dynamic_filters=0 begin exec @retcode = sp_MSgettablecontents @pubid if @@ERROR<>0 OR @retcode <>0 return (1) end exec('select * from #temp_table_for_systable_view ') drop table #temp_table_for_systable_view return (0) go exec dbo.sp_MS_marksystemobject sp_MSmakesystableviews go grant exec on dbo.sp_MSmakesystableviews to public go raiserror('Creating procedure sp_MSgetchangecount', 0,1) GO create procedure sp_MSgetchangecount( @startgen int, @changes int output, @updates int output, @deletes int output) as select @deletes = count(*) from dbo.MSmerge_tombstone where generation = 0 or generation > @startgen select @updates = count(*) from dbo.MSmerge_contents where generation = 0 or generation > @startgen select @changes = @updates + @deletes return (0) go exec dbo.sp_MS_marksystemobject sp_MSgetchangecount go grant exec on dbo.sp_MSgetchangecount to public raiserror('Creating procedure sp_MSbelongs', 0,1) GO -- Modify temp table. No security check needed. create procedure sp_MSbelongs @publisher sysname, @publisher_db sysname, @publication sysname, @tablenick int, @rowguid uniqueidentifier, @retval int output, @nested int = 0 AS declare @artid uniqueidentifier declare @join_guid uniqueidentifier declare @last_joinid int declare @join_id int declare @join_nick int declare @probe_id int declare @last_probe int declare @join_nickstr nvarchar(10) declare @pubid uniqueidentifier declare @guidstring nvarchar(38) declare @subset_filter nvarchar(4000) declare @tablename nvarchar(266) declare @join_table nvarchar(266) declare @boolean nvarchar(4000) declare @retcode smallint select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db /* Check for case of all rows - do I trust subset_type ? */ select @boolean = subset_filterclause, @artid = artid from sysmergearticles where pubid = @pubid and nickname = @tablenick if ((@boolean is null or @boolean = ' ') and not exists (select * from sysmergesubsetfilters where art_nickname = @tablenick)) begin set @retval = 0 return end if @nested = 0 begin create table #found (flag int NOT NULL) create table #probe (probe_id int identity NOT NULL, tablenick int NOT NULL, rowguid uniqueidentifier ROWGUIDCOL default newid() not null, tested int NOT NULL) insert into #found values (0) set @last_probe = 0 end else begin select @last_probe = max(probe_id) from #probe end /* pubid is already available */ exec @retcode = dbo.sp_MStablenamefromnick @tablenick, @tablename out, @pubid if @@ERROR<>0 or @retcode<>0 return (1) set @guidstring = '''' + convert(nchar(36), @rowguid) + '''' /* If there is boolean filter, check for it being satisfied */ if @boolean is not null and @boolean <> ' ' begin exec ('if exists (select * from ' + @tablename + ' where rowguidcol = ' + @guidstring + ' and (' + @boolean + ')) update #found set flag = 1') if @@ERROR<>0 return (1) select @retval = flag from #found if @retval = 1 goto EndLabel end /* Loop over join filters, populating #probe */ select @join_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick while @join_id is not null begin select @boolean = join_filterclause, @join_nick = join_nickname from sysmergesubsetfilters where pubid = @pubid and join_filterid = @join_id exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @join_table out, @pubid if @@ERROR<>0 or @retcode<>0 return (1) set @join_nickstr = convert(nchar(10), @join_nick) /* execute a query to put these into the #probe table */ exec ('insert into #probe (tablenick, rowguid, tested) select distinct ' + @join_nickstr + ', ' + @join_table + '.rowguidcol, 0 from ' + @tablename + ', ' + @join_table + ' where ' + @tablename + '.rowguidcol = ' + @guidstring + ' and (' + @boolean + ') and not exists (select * from #probe where tablenick = ' + @join_nickstr + ' and rowguidcol = ' + @join_table + '.rowguidcol) ' ) if @@ERROR<>0 begin return (1) end /* get to next join filter and repeat */ set @last_joinid = @join_id select @join_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick and join_filterid > @last_joinid end /* Loop over probe, making recursive call */ select @probe_id = min(probe_id) from #probe where probe_id > @last_probe and tested = 0 while @probe_id is not null begin select @join_nick = tablenick, @join_guid = rowguidcol from #probe where probe_id = @probe_id set @last_probe = @probe_id /* update tested flag on this row so we don't try it again while recursing */ update #probe set tested = 1 where probe_id = @probe_id /* Make recursive call. If it belongs, we are done. */ exec @retcode = dbo.sp_MSbelongs @publisher, @publisher_db, @publication, @join_nick, @join_guid, @retval output, 1 if @@ERROR<>0 OR @retcode<>0 begin return (1) end if @retval = 1 goto EndLabel /* get next probe_id and repeat */ select @probe_id = min(probe_id) from #probe where probe_id > @last_probe and tested = 0 end /* All Done, delete temps if not nested */ EndLabel: if @nested = 0 begin drop table #found drop table #probe end return go exec dbo.sp_MS_marksystemobject sp_MSbelongs go grant exec on dbo.sp_MSbelongs to public go raiserror('Creating procedure sp_MSexpandbelongs', 0,1) GO -- Modify temp table. No security check needed. create procedure sp_MSexpandbelongs @pubid uniqueidentifier AS declare @filterid int declare @retval int declare @expand_proc sysname /* We iterate over the join filters */ --select @filterid = min(flag) from #belong select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0) while (@filterid is not null) begin select @expand_proc = expand_proc from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid exec @retval = @expand_proc @belong = 1 if @@error<>0 or @retval <> 0 return (1) select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0) end return (0) go exec dbo.sp_MS_marksystemobject sp_MSexpandbelongs go grant exec on dbo.sp_MSexpandbelongs to public go raiserror('Creating procedure sp_MSexpandnotbelongs', 0,1) GO -- Modify temp table. No security check needed. create procedure sp_MSexpandnotbelongs @pubid uniqueidentifier AS declare @filterid int declare @retval int declare @expand_proc sysname /* We iterate over the join filters */ --select @filterid = min(flag) from #notbelong select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid) while (@filterid is not null) begin select @expand_proc = expand_proc from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid exec @retval = @expand_proc @belong = 0 if @@error<>0 or @retval <> 0 return (1) select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid) end go exec dbo.sp_MS_marksystemobject sp_MSexpandnotbelongs go grant exec on dbo.sp_MSexpandnotbelongs to public go raiserror('Creating procedure sp_MSsetupbelongs_withoutviewproc', 0,1) GO create procedure sp_MSsetupbelongs_withoutviewproc @publisher sysname, @publisher_db sysname, @publication sysname, @artnick int AS declare @temp_id int declare @artnickstr nvarchar(10) declare @retval int declare @tablenick int declare @rowguid uniqueidentifier declare @rowguidstr nvarchar(40) declare @partchangegen int declare @joinchangegen int declare @retcode smallint set @artnickstr = convert(nchar(10), @artnick) delete from #temp_cont if exists(select * from #genlist) begin exec ('insert into #temp_cont (tablenick, rowguid, partchangegen, joinchangegen) select tablenick, rowguid, partchangegen, joinchangegen from dbo.MSmerge_contents where tablenick = ' + @artnickstr + ' and generation in (select generation from #genlist)') if @@ERROR <>0 begin return (1) end end set @temp_id = 0 select @temp_id = min(temp_id) from #temp_cont where temp_id > @temp_id while (@temp_id is not null) begin select @tablenick = tablenick, @rowguid = rowguid, @partchangegen = partchangegen, @joinchangegen = joinchangegen from #temp_cont where temp_id = @temp_id set @rowguidstr = '''' + convert(nchar(36), @rowguid) + '''' exec @retcode = dbo.sp_MSbelongs @publisher, @publisher_db, @publication, @tablenick, @rowguid, @retval output, 0 if @@ERROR<>0 OR @retcode <>0 begin return (1) end if @retval = 1 begin insert into #belong (tablenick, rowguid, flag, skipexpand, partchangegen, joinchangegen) values (@artnick, @rowguid, 0, 0, @partchangegen, @joinchangegen) if @@ERROR <>0 begin return (1) end end select @temp_id = min(temp_id) from #temp_cont where temp_id > @temp_id end return 0 GO exec dbo.sp_MS_marksystemobject sp_MSsetupbelongs_withoutviewproc go grant exec on dbo.sp_MSsetupbelongs_withoutviewproc to public go raiserror('Creating procedure sp_MSsetupnotbelongs', 0,1) GO create procedure sp_MSsetupnotbelongs @artnick int, @before_view_objid int, @before_table_objid int, @rgcol sysname, @commongen int AS declare @before_view_name sysname declare @before_table_name sysname declare @artnickstr nvarchar(10) declare @commongenstr nvarchar(12) set @artnickstr = convert(nvarchar(10), @artnick) set @commongenstr = convert(nvarchar(12), @commongen) /* Put changes in #notbelong that aren't in #belong and have a relevant partchangegen */ -- If publication has before image tables, we should screen changes using the before image tables --rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ') and if @before_view_objid is not null begin set @before_view_name = OBJECT_NAME(@before_view_objid) set @before_table_name = OBJECT_NAME(@before_table_objid) execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen) select tablenick, rowguid, 0, partchangegen, joinchangegen from #contents_subset where partchangegen > ' + @commongenstr + ' and tablenick = ' + @artnickstr + ' and (rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + ') or (rowguid in (select ' + @rgcol + ' from ' + @before_table_name + ' where system_delete = 1 and generation > ' + @commongenstr + ') and (rowguid not in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + ')))) and rowguid not in (select rowguid from #belong) ') if @@ERROR <>0 begin return (1) end if exists (select * from #genlist) begin /* Add tombstones to ##notbelong */ execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) select tablenick, rowguid, 0, generation, generation, type from #tombstone_subset where tablenick = ' + @artnickstr + ' and (type = 6 or rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + '))') if @@ERROR <>0 begin return (1) end end end else begin insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen) select tablenick, rowguid, 0, partchangegen, joinchangegen from #contents_subset where partchangegen > @commongen and tablenick = @artnick and rowguid not in (select rowguid from #belong) if @@ERROR <>0 begin return (1) end if exists (select * from #genlist) begin insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) select tablenick, rowguid, 0, generation, generation, type from #tombstone_subset where tablenick = @artnick if @@ERROR <>0 begin return (1) end end end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSsetupnotbelongs go grant exec on dbo.sp_MSsetupnotbelongs to public go raiserror('Creating procedure sp_MSsetupworktables', 0,1) GO -- Modify temp table. No security check needed. create procedure sp_MSsetupworktables @pubid uniqueidentifier, @genlist varchar(8000), @articlesoption int, -- 0=process all articles, 1=process this specific article (whose nickname is passed in @tablenickname), 2=all articles involved in join filters, 3=process all articles involved in part filters, 4=process articles whose nicknames have been passed in @nicknamelist. @tablenickname int, @nicknamelist varchar(8000), @mingen int = 0, @maxgen int = 0, @skipgenlist varchar(8000) = NULL, @contents_subset_rowcount int OUTPUT, @tombstone_subset_rowcount int OUTPUT AS declare @lengenlist int declare @lenskipgenlist int -- put in a nickname with value 0 - This will match -- gen history rows put in by downlevel subscribers -- with art_nick = NULL insert into #nicknames_to_process values (0) if (@articlesoption = 0) begin -- process all articles for this publication insert into #nicknames_to_process select distinct nickname from sysmergearticles a where pubid = @pubid end else if (@articlesoption = 1) begin --process only the article whose nickname has been passed-in insert into #nicknames_to_process values (@tablenickname) end else if (@articlesoption = 2) begin --process all articles with join filters (article could be on any side - left or right - of any join filter) insert into #nicknames_to_process select distinct nickname from sysmergearticles a where pubid = @pubid and exists (select * from sysmergesubsetfilters s where s.pubid = @pubid and (s.art_nickname = a.nickname or s.join_nickname = a.nickname)) end else if (@articlesoption = 3) begin --process all articles with part filters but that don't have a join filter. insert into #nicknames_to_process select distinct nickname from sysmergearticles a where pubid = @pubid and datalength(subset_filterclause) > 1 and not exists (select * from sysmergesubsetfilters s where s.pubid = @pubid and (s.art_nickname = a.nickname or s.join_nickname = a.nickname)) end else if (@articlesoption = 4) begin --process all articles whose nicknames have been passed into @nicknamelist if (@nicknamelist is not null and rtrim(ltrim(@nicknamelist)) <> '') begin exec ('insert into #nicknames_to_process select distinct nickname from sysmergearticles where nickname in (' + @nicknamelist + ')') if @@ERROR <>0 return (1) end end -- Create index on #nicknames_to_process now that it has been populated. Creating it after data insertion is better because -- that generates the stats for the index. That helps in the insert into #contents_subset query. The other option was to -- create the index, insert the data, and then update statistics. create unique index #nicknames_ind on #nicknames_to_process (nickname) if (@maxgen <> 0) begin insert into #genlist select distinct generation from dbo.MSmerge_genhistory gh join #nicknames_to_process np on isnull(gh.art_nick,0) = np.nickname and gh.generation >= @mingen and gh.generation <= @maxgen select @lengenlist = isnull(datalength(@genlist),0) select @lenskipgenlist = isnull(datalength(@skipgenlist),0) -- no need to do ltrim and rtrim on the @skipgenlist. sp_MSsetupbelongs already did that. if (@skipgenlist is not null and @skipgenlist <> '' and @lenskipgenlist <= @lengenlist) begin exec('delete from #genlist where generation in (' + @skipgenlist + ')') end else if (@genlist is not null and @genlist <> '') begin -- gen 0 won't be in @genlist, so will get deleted because of the NOT IN. exec('delete from #genlist where generation not in (' + @genlist + ')') end end else if (@genlist is not null and @genlist <> '') begin exec ('insert into #genlist select distinct generation from dbo.MSmerge_genhistory where (isnull(art_nick,0) in (select nickname from #nicknames_to_process)) and generation in (' + @genlist + ') ') if @@ERROR <>0 return (1) end -- Create index on #genlist now that it has been populated. Creating it after data insertion is better because -- that generates the stats for the index. That helps in the insert into #contents_subset query. The other option was to -- create the index, insert the data, and then update statistics. create unique index #genlist_ind on #genlist (generation) if @maxgen is null select @maxgen = 0 if @mingen is null select @mingen = 0 if (@maxgen = 0) begin -- SQL 7.0 pull merge agents do not pass in @maxgen and @mingen, so the default is 0. -- For them need to compute min and max. Note that we could do the same for 8.0 agents as well -- but 8.0 agents pass the min and max because they are used in the INSERT INTO #genlist query above -- when we don't have the #genlist table and all we have is the comma-separated @genlist string. select @mingen = min(generation), @maxgen = max(generation) from #genlist end insert into #contents_subset (tablenick, rowguid, generation, partchangegen, joinchangegen) select c.tablenick, c.rowguid, c.generation, c.partchangegen, c.joinchangegen from dbo.MSmerge_contents c JOIN #nicknames_to_process a ON c.generation >= @mingen AND c.generation <= @maxgen AND c.tablenick = a.nickname JOIN #genlist g ON c.generation = g.generation AND c.generation >= @mingen AND c.generation <= @maxgen UNION -- the rows retrieved below have a partition change that falls into the relevant generation range -- if we do not process them now, we would later on assume that the partition change has already been processed select c.tablenick, c.rowguid, c.generation, c.partchangegen, c.joinchangegen from dbo.MSmerge_contents c JOIN #nicknames_to_process a ON c.partchangegen >= @mingen AND c.partchangegen <= @maxgen AND c.tablenick = a.nickname JOIN #genlist g ON c.partchangegen = g.generation AND c.partchangegen >= @mingen AND c.partchangegen <= @maxgen select @contents_subset_rowcount = @@rowcount insert into #tombstone_subset (tablenick, rowguid, type, generation) select t.tablenick, t.rowguid, t.type, t.generation from dbo.MSmerge_tombstone t JOIN #nicknames_to_process a ON t.generation >= @mingen AND t.generation <= @maxgen AND t.tablenick = a.nickname JOIN #genlist g ON t.generation = g.generation AND t.generation >= @mingen AND t.generation <= @maxgen select @tombstone_subset_rowcount = @@rowcount create clustered index #ucind_contents_subset on #contents_subset (tablenick, rowguid) with FILLFACTOR = 100 create clustered index #ucind_tombstone_subset on #tombstone_subset (tablenick, rowguid) with FILLFACTOR = 100 delete #nicknames_to_process from #nicknames_to_process ntp where not exists (select tablenick from #contents_subset cs where cs.tablenick = ntp.nickname) and not exists (select tablenick from #tombstone_subset ts where ts.tablenick = ntp.nickname) -- remove the nickname with value 0 that we put in specially at the beginning of this proc. delete from #nicknames_to_process where nickname = 0 return (0) GO exec dbo.sp_MS_marksystemobject sp_MSsetupworktables go grant exec on dbo.sp_MSsetupworktables to public go raiserror('Creating procedure sp_MSsetupbelongs', 0,1) GO -- Modify temp table. No security check needed. create procedure sp_MSsetupbelongs @publisher sysname, @publisher_db sysname, @publication sysname, @genlist varchar(8000), @commongen int, @subissql int, @articlesoption int=0, -- 0=process all articles, 1=process this specific article (whose nickname is passed in @tablenickname), 2=all articles involved in join filters, 3=process all articles involved in part filters, 4=process articles whose nicknames have been passed in @nicknamelist. @tablenickname int=0, @handle_null_tables bit=0, -- 0=caller cannot handle NULL ##belongs and ##notbelongs tables, 1=caller handles NULL ##belongs and ##notbelongs tables (post 8.0 Beta 2 version) @nicknamelist varchar(8000) = NULL, @mingen int = 0, @maxgen int = 0, @skipgenlist varchar(8000) = NULL AS declare @pubid uniqueidentifier declare @retval int declare @tablenick int declare @rowguid uniqueidentifier declare @rowguidstr nvarchar(40) declare @belongsname sysname declare @notbelongsname sysname declare @artnick int declare @before_view_objid int declare @before_table_objid int declare @procname sysname declare @artbaseobjid int declare @rgcol sysname declare @maxfilterid int declare @retcode smallint declare @dynamic_join_cnt int declare @contents_subset_rowcount int declare @tombstone_subset_rowcount int declare @belongsempty bit declare @notbelongsempty bit -- trim spaces from the generation lists so that we don't have to use functions ltrim -- and rtrim again and again later on. select @genlist = ltrim(rtrim(@genlist)) select @skipgenlist = ltrim(rtrim(@skipgenlist)) select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db set @rowguid = newid() select @rowguidstr = replace( convert( nvarchar(36), @rowguid ), '-', '' ) set @belongsname = '##belong' + @rowguidstr set @notbelongsname = '##notbelong' + @rowguidstr -- since the belongsname and notbelongsname names have guids appended to them generated using newid(), we can safely assume -- that the names are unique. create table #genlist (generation int) create table #temp_cont (temp_id int identity NOT NULL, tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, partchangegen int null, joinchangegen int null) create table #contents_subset(tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NOT NULL, partchangegen int NULL, joinchangegen int NULL) create table #tombstone_subset(tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, type tinyint NOT NULL, generation int NOT NULL) create table #belong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL, partchangegen int null, joinchangegen int null, skipexpand bit NOT NULL) create table #notbelong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL, partchangegen int null, joinchangegen int null, type tinyint default 5) create table #nicknames_to_process (nickname int) create index #indbelong on #belong (rowguid, tablenick) create index #indnbelong on #notbelong (tablenick, rowguid) exec ('create table ' + @belongsname + ' (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(255) NULL, colv varbinary(2048) NULL)') if @@ERROR <>0 return (1) exec ('create table ' + @notbelongsname + ' (bookmark int identity unique NOT NULL, tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(255) NULL, type tinyint NOT NULL)') if @@ERROR <>0 return (1) -- Fully qualifying the proc avoids recompiles exec @retcode = dbo.sp_MSsetupworktables @pubid, @genlist, @articlesoption, @tablenickname, @nicknamelist, @mingen, @maxgen, @skipgenlist, @contents_subset_rowcount OUTPUT, @tombstone_subset_rowcount OUTPUT IF @@ERROR<>0 OR @retcode<>0 return (1) if (@contents_subset_rowcount = 0 and @tombstone_subset_rowcount = 0) goto EXITPROC if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4) begin select @dynamic_join_cnt = count(*) from (select join_filterclause_spaces_stripped = REPLACE(REPLACE(REPLACE(REPLACE(UPPER(join_filterclause collate SQL_Latin1_General_CP1_CS_AS), char(0x20),''), char(0x09),''), char(0x0D),''), char(0x0A),'') from sysmergesubsetfilters where pubid = @pubid) as sysmergesubsetfilters_temp where sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%NAME()%' or sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%ID()%' or sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SESSION[_]USER%' or sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SYSTEM[_]USER%' end /* step 2 setup pass through dbo.MSmerge_contents */ /* article with permanent views can be handled with bulk inserts */ set @artnick = NULL set rowcount 0 /* Get first article, go into loop */ select @artnick = min(nickname) from #nicknames_to_process while (@artnick is not null) begin select @artbaseobjid = objid, @procname = view_sel_proc, @before_view_objid = before_view_objid, @before_table_objid = before_image_objid from sysmergearticles where pubid = @pubid and nickname = @artnick /* Get name of rowguidcol. Aliasing doesn't work through a view. */ select @rgcol = name from syscolumns where id = @artbaseobjid and ColumnProperty(@artbaseobjid, name, 'isrowguidcol') = 1 if (@procname is not null) begin -- Fully qualifying the proc avoids recompiles select @procname = 'dbo.' + @procname exec @retcode = @procname @artnick if @@ERROR <>0 or @retcode <> 0 begin return (1) end end else begin -- Fully qualifying the proc avoids recompiles exec @retcode = dbo.sp_MSsetupbelongs_withoutviewproc @publisher, @publisher_db, @publication, @artnick if @@ERROR <>0 or @retcode <> 0 return (1) end -- Fully qualifying the proc avoids recompiles exec @retcode = dbo.sp_MSsetupnotbelongs @artnick, @before_view_objid, @before_table_objid, @rgcol, @commongen if @@ERROR <>0 or @retcode <> 0 return (1) /* Move on to next article, repeat while loop */ select @artnick = min(nickname) from #nicknames_to_process where nickname > @artnick end if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4) begin /* Optimization: If joinchangegen and partchange are both null or < common gen, ** it is not necessary to expand #belong for that particular row. */ select @maxfilterid = max(join_filterid) from sysmergesubsetfilters if @maxfilterid is not null begin update #belong set flag = @maxfilterid, skipexpand = 1 where isnull(joinchangegen,0) <= @commongen and isnull(partchangegen,0) <= @commongen /* Expand the #belong temptable */ exec @retcode = dbo.sp_MSexpandbelongs @pubid if @@ERROR<>0 OR @retcode<>0 begin return (1) end end end /* If subscriber is sql server, we don't have to expand belongs */ if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4) and (@subissql = 0 or @dynamic_join_cnt > 0) begin /* Expand the #notbelong temptable */ exec dbo.sp_MSexpandnotbelongs @pubid if @@error<>0 return(1) end EXITPROC: /* transfer rows from local temp to global temp */ -- Could have used if not exists instead of the following but want to use the KEEP PLAN option which is not supported in the IF EXISTS select @belongsempty = 0 select @belongsempty = 1 where not exists (select * from #belong) OPTION(KEEP PLAN) select @notbelongsempty = 0 select @notbelongsempty = 1 where not exists (select * from #notbelong) OPTION(KEEP PLAN) /* If there are no rows in #belong, then drop the global ##belongs so that we do not call sp_MSenumpartialchanges */ if (@belongsempty = 1) begin /* Post SQL 8.0 Beta 2 agents pass this flag with value 1 since they can handle NULL belongs table name */ if @handle_null_tables = 1 begin exec ('drop table ' + @belongsname) select @belongsname = NULL end end else begin exec ('insert into ' + @belongsname + ' (tablenick, rowguid, generation, lineage, colv) select distinct b.tablenick, b.rowguid, c.generation, c.lineage, c.colv1 from #belong b left outer join dbo.MSmerge_contents c on c.tablenick = b.tablenick and c.rowguid = b.rowguid ') if @@ERROR <>0 begin return (1) end -- this index will be useful in sp_MSenumpartialchanges exec ('create index nc1belongstable on ' + @belongsname + ' (tablenick, rowguid) with FILLFACTOR = 100') if @@ERROR <>0 return (1) end /* If there are no rows in #notbelong, then drop the global ##notbelongs so that we do not call sp_MSenumpartialchanges */ if (@notbelongsempty = 1) begin /* Post SQL 8.0 Beta 2 agents pass this flag with value 1 since they can handle NULL notbelongs table name */ if @handle_null_tables = 1 begin exec ('drop table ' + @notbelongsname) select @notbelongsname = NULL end end else begin /* transfer rows from local temp to global temp */ exec ('insert into ' + @notbelongsname + ' (tablenick, rowguid, generation, lineage, type) select distinct b.tablenick, b.rowguid, coalesce (c.generation, t.generation), coalesce(c.lineage, t.lineage), b.type from #notbelong b left outer join dbo.MSmerge_contents c on c.tablenick = b.tablenick and c.rowguid = b.rowguid left outer join dbo.MSmerge_tombstone t on t.tablenick = b.tablenick and t.rowguid = b.rowguid order by b.tablenick DESC, b.rowguid ASC ') if @@ERROR <>0 begin return (1) end -- this index will be useful in sp_MSenumpartialdeletes exec ('create index nc1notbelongstable on ' + @notbelongsname + ' (tablenick DESC, rowguid) with FILLFACTOR = 100') if @@ERROR <>0 return (1) end if (@belongsempty = 1) select @belongsname, @notbelongsname, -1 else begin if (@articlesoption = 1) begin select @belongsname, @notbelongsname, @tablenickname end else begin select distinct @belongsname, @notbelongsname, tablenick from #belong end end drop table #notbelong drop table #belong drop table #contents_subset drop table #tombstone_subset drop table #temp_cont drop table #nicknames_to_process return (0) go exec dbo.sp_MS_marksystemobject sp_MSsetupbelongs go grant exec on dbo.sp_MSsetupbelongs to public go raiserror('Creating procedure sp_MSaddinitialarticle', 0,1) GO -- Called at the subscriber CREATE PROCEDURE sp_MSaddinitialarticle( @article sysname, /* Name of the article */ @artid uniqueidentifier, /* Article ID */ @pubid uniqueidentifier, /* Publication ID */ @nickname int, /* Article nickname */ @column_tracking int, /* Does the article have column tracking ? */ @status int, /* Status of the article */ @pre_creation_command int = 0, /* Precreate command of the article */ @resolver_clsid nvarchar(255) = NULL,/* Resolver module for the article */ @insert_proc nvarchar(255) = NULL,/* Insert sp for article */ @update_proc nvarchar(255) = NULL,/* Update sp for article */ @select_proc nvarchar(255) = NULL, /* Select SP for this article */ @destination_object sysname, /* Destination object name */ @missing_count int = NULL, @missing_cols varbinary(32) = NULL, @article_resolver nvarchar(255) = NULL, @resolver_info nvarchar(255) = NULL, @filter_clause nvarchar(2000) = NULL, @excluded_count int = NULL, @excluded_cols varbinary(32) = NULL, @destination_owner sysname = NULL, @identity_support int = 0, @verify_resolver_signature int = 0, /* 0=do not verify signature, 1=verify that signature is from trusted source, more values may be added later */ @fast_multicol_updateproc bit = 0 ) AS SET NOCOUNT ON declare @objid int declare @sub_missing_cols binary(32) declare @retcode int /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) if (@artid is NULL) BEGIN RAISERROR (14057, 16, -1) RETURN (1) END /* ** The columns that do not belong to the partition at subscriber side are nothing but ** missing columns to the subscriber side - it just does not have them ! ** For the same reason, the excluded ones from publisher does not mean anything to ** subscriber, which does not exclude anything for itself. We just reset to 0 */ select @missing_count = @missing_count + @excluded_count exec @retcode= master..xp_ORbitmap @missing_cols, @excluded_cols, @sub_missing_cols OUTPUT if @@error<>0 or @retcode<>0 return(1) if (@resolver_clsid='') select @resolver_clsid = NULL if (@filter_clause='') set @filter_clause = NULL /* ** Populate the local copy of sysmergearticles */ if exists (select * from sysmergearticles where artid = @artid and pubid = @pubid ) begin update sysmergearticles set name = @article, artid = @artid, pre_creation_command = pre_creation_command, pubid = @pubid, nickname = @nickname, column_tracking = @column_tracking, status = @status, resolver_clsid = @resolver_clsid, insert_proc = @insert_proc, update_proc = @update_proc, select_proc = @select_proc, destination_object = @destination_object, destination_owner = @destination_owner, missing_col_count = @missing_count, missing_cols = @sub_missing_cols, article_resolver = @article_resolver, resolver_info = @resolver_info, subset_filterclause = @filter_clause, excluded_col_count = 0, excluded_cols = 0x00, identity_support=@identity_support, verify_resolver_signature = @verify_resolver_signature, fast_multicol_updateproc = @fast_multicol_updateproc where artid = @artid and pubid = @pubid end else begin select @objid = 0 insert sysmergearticles (name, type, objid, sync_objid, artid, pre_creation_command, pubid, nickname, column_tracking, status, resolver_clsid, destination_owner, insert_proc, update_proc, select_proc, destination_object, missing_col_count, missing_cols, article_resolver, resolver_info, subset_filterclause, excluded_col_count, excluded_cols, identity_support, verify_resolver_signature, fast_multicol_updateproc) values (@article, 0x0a, @objid, @objid, @artid, @pre_creation_command, @pubid, @nickname, @column_tracking, @status, @resolver_clsid, @destination_owner, @insert_proc, @update_proc, @select_proc, @destination_object, @missing_count, @sub_missing_cols, @article_resolver, @resolver_info, @filter_clause, 0, 0x00, @identity_support, @verify_resolver_signature, @fast_multicol_updateproc) end IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) RETURN (1) END RETURN 0 go exec dbo.sp_MS_marksystemobject sp_MSaddinitialarticle go grant exec on dbo.sp_MSaddinitialarticle to public go raiserror('Creating procedure sp_MSaddinitialschemaarticle', 0,-1) go CREATE PROCEDURE sp_MSaddinitialschemaarticle( @name sysname, @destination_object sysname, @destination_owner sysname, @artid uniqueidentifier, @pubid uniqueidentifier, @pre_creation_command tinyint, @status int, @type tinyint )as begin set nocount on declare @objid int declare @old_objid int declare @retcode int declare @qualified_name nvarchar(270) /* ** Security check */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return(1) if (@artid is NULL) begin raiserror (14057, 16, -1) return 1 end if @destination_owner is null or @destination_owner = '' begin select @destination_owner = user_name() end select @qualified_name = quotename(@destination_owner) + '.' + quotename(@destination_object) select @objid = object_id(@qualified_name) select @old_objid = null select @old_objid = objid from sysmergeschemaarticles where artid = @artid and pubid = @pubid -- Update the objid field of all merge schema articles with the same article id update sysmergeschemaarticles set objid = @objid where artid = @artid -- Update the objid field of all transactional schema articles with the same article id or same old objid if @old_objid is not null and @objid <> @old_objid begin if exists (select * from sysobjects where name = 'sysschemaarticles') begin update sysschemaarticles set objid = @objid where objid = @old_objid end end begin transaction if exists (select * from sysmergeschemaarticles where artid = @artid and pubid = @pubid) begin update dbo.sysmergeschemaarticles set name = @name, destination_object = @destination_object, destination_owner = @destination_owner, pre_creation_command = @pre_creation_command, status = @status, type = @type where artid = @artid and pubid = @pubid if @@error<>0 begin rollback transaction return 1 end end else begin insert dbo.sysmergeschemaarticles (name, type, objid, artid, description, pre_creation_command, pubid, status, creation_script, schema_option, destination_object, destination_owner) values (@name, @type, @objid, @artid, NULL, @pre_creation_command, @pubid, @status, NULL, 0x0000000000000000, @destination_object, @destination_owner) if @@error<>0 begin rollback transaction return 1 end end exec @retcode = dbo.sp_MSmarkschemaobject @destination_object, @destination_owner if @@error<>0 or @retcode<>0 begin rollback transaction return 1 end commit transaction return 0 end go exec dbo.sp_MS_marksystemobject sp_MSaddinitialschemaarticle go grant exec on dbo.sp_MSaddinitialschemaarticle to public go raiserror('Creating procedure sp_MSaddinitialpublication', 0,1) GO CREATE PROCEDURE sp_MSaddinitialpublication( @publisher sysname, @publisher_db sysname, @publication sysname, /* Name of the publication */ @description nvarchar(255), /* Description of the publication */ @pubid uniqueidentifier, /* Publication ID */ @retention int, /* Retention period of the publication */ @sync_mode int, /* Sync mode of the publication */ @allow_push int, /* does publication allow push ? */ @allow_pull int, /* does publication allow pull ? */ @allow_anonymous int, /* does publication allow anonymous ? */ @centralized_conflicts int, /* publication does centralized conflicts ? */ @status int, /* publication's status */ @snapshot_ready int, /* publication snapshto_ready flag ? */ @enabled_for_internet int, /* publication enabled_for_internet flag ? */ @publication_type int, /* a full publication or a partial one */ @conflict_retention int = 60, /* the retention period for conflict table */ @allow_subscription_copy int = 0, /* does publication allow subscription copies to sync ? */ @allow_synctoalternate int = 0, /* does publication allow subscription to sync to alternates ? */ @backward_comp_level int = 10 /* default to 7.0 server */ ) AS SET NOCOUNT ON declare @retcode int declare @publisher_srvid int select @publication = RTRIM(@publication) select @publisher_db = RTRIM(@publisher_db) /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default if @publisher_srvid is NULL begin EXECUTE @retcode = dbo.sp_addserver @publisher, @duplicate_ok='duplicate_ok' IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14042, 16, -1) RETURN (1) END end select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default IF @publisher_srvid IS NULL BEGIN RAISERROR (14010, 16, -1) RETURN (1) END /* ** Populate the local copy of sysmergepublications */ BEGIN TRAN save TRAN MSaddinitialpublication if exists (select * from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db) begin declare @pubid_local uniqueidentifier /* select the local pubid only if it has a valid parentid */ select @pubid_local = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db if exists (select * from sysmergesubscriptions where pubid = @pubid and srvid = @publisher_srvid and db_name = @publisher_db) begin delete from sysmergesubscriptions where pubid = @pubid and srvid = @publisher_srvid and db_name = @publisher_db IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto FAILURE END end update sysmergesubscriptions SET pubid = @pubid where pubid = @pubid_local IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto FAILURE END if @pubid <> @pubid_local delete from sysmergesubscriptions where subid = @pubid update sysmergesubscriptions SET subid = @pubid where subid = @pubid_local IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto FAILURE END update sysmergesubscriptions SET partnerid = @pubid where partnerid = @pubid_local IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto FAILURE END update sysmergepublications SET pubid = @pubid, name = @publication, description = @description, designmasterid = @pubid, retention = @retention, parentid = pubid, sync_mode = sync_mode, allow_push = @allow_push, allow_pull = @allow_pull, allow_anonymous = @allow_anonymous, centralized_conflicts = @centralized_conflicts, status = @status, snapshot_ready = @snapshot_ready, enabled_for_internet = @enabled_for_internet, publication_type = @publication_type, conflict_retention = @conflict_retention, allow_subscription_copy = @allow_subscription_copy, allow_synctoalternate = @allow_synctoalternate , backward_comp_level = @backward_comp_level where name = @publication and UPPER(publisher) = UPPER(@publisher) and publisher_db = @publisher_db end else begin insert sysmergepublications(publisher, publisher_db,pubid, name, description, designmasterid, retention, parentid, sync_mode, allow_push, allow_pull, allow_anonymous, centralized_conflicts, status, snapshot_ready, enabled_for_internet, publication_type, conflict_retention, allow_subscription_copy, allow_synctoalternate, backward_comp_level) values(@publisher, @publisher_db, @pubid, @publication, @description, @pubid, @retention, @pubid, @sync_mode, @allow_push, @allow_pull, @allow_anonymous, @centralized_conflicts, @status, @snapshot_ready, @enabled_for_internet, @publication_type, @conflict_retention, @allow_subscription_copy, @allow_synctoalternate, @backward_comp_level) end IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto FAILURE END COMMIT TRAN RETURN (0) FAILURE: /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION MSaddinitialpublication COMMIT TRANSACTION end RETURN (1) go exec dbo.sp_MS_marksystemobject sp_MSaddinitialpublication go grant exec on dbo.sp_MSaddinitialpublication to public go raiserror('Creating procedure sp_MSaddinitialsubscription', 0,1) GO CREATE PROCEDURE sp_MSaddinitialsubscription( @pubid uniqueidentifier, /* Publication ID */ @subid uniqueidentifier, /* Subscription's replica ID */ @partnerid uniqueidentifier, /* Partner's replica ID */ @subscriber sysname, /* Subscriber server */ @subscriber_db sysname, /* Subscriber database */ @subscriber_priority real = 0.0, /* Subscriber priority */ @subscriber_type tinyint = 0, /* Subscriber type - local, global, or anonymous */ @subscription_type int = 0, /* Subscription type - push or pull */ @sync_type tinyint = 2, /* Subscription sync type 1 = no sync, 2 = automatic */ @publication sysname = NULL, /* Publication Name */ @distributor sysname = NULL /* Distributor */ ) AS SET NOCOUNT ON /* ** Declarations. */ DECLARE @local tinyint DECLARE @anonymous tinyint DECLARE @subscriber_srvid int DECLARE @subnickname int DECLARE @active tinyint DECLARE @retcode int DECLARE @subid_old uniqueidentifier /* ** Initializations */ SET @local = 2 SET @anonymous = 3 set @active = 1 /* after this SP is called, the subscription is activated */ select @subscriber_db = RTRIM(@subscriber_db) /* ** Check for subscribing permission ** It is called by merge agent at the publisher side ** subscriber side? */ -- @pubid is not local if sessionproperty('replication_agent') = 0 begin exec @retcode= dbo.sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end -- this gets executed at both publisher (for pull subscriptions) and at subscriber -- if server is not found use srvid of 0 for local srvid select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default if @subscriber_srvid is null set @subscriber_srvid = 0 begin tran save TRAN MSaddinitialsubscription /* ** Populate the local copy of sysmergesubscriptions */ if exists (select * from sysmergesubscriptions where subid = @subid) begin update sysmergesubscriptions SET subid = @subid, datasource_type = 0, datasource_path = NULL, srvid = @subscriber_srvid, db_name = @subscriber_db, pubid = @pubid, status = @active, subscriber_type = @subscriber_type, subscription_type = @subscription_type, priority = @subscriber_priority, sync_type = @sync_type, subscriber_server = @subscriber, publication = @publication, distributor = @distributor where subid = @subid IF @@ERROR <> 0 BEGIN goto FAILURE END end if exists (select * from sysmergesubscriptions where srvid = @subscriber_srvid AND db_name = @subscriber_db AND pubid = @pubid) begin select @subid_old = subid from sysmergesubscriptions where srvid = @subscriber_srvid AND db_name = @subscriber_db AND pubid = @pubid update sysmergesubscriptions SET subid = @subid, datasource_type = 0, datasource_path = NULL, srvid = @subscriber_srvid, db_name = @subscriber_db, pubid = @pubid, status = @active, subscriber_type = @subscriber_type, subscription_type = @subscription_type, priority = @subscriber_priority, sync_type = @sync_type, subscriber_server = @subscriber, publication = @publication, distributor = @distributor where srvid = @subscriber_srvid AND db_name = @subscriber_db AND pubid = @pubid IF @@ERROR <> 0 BEGIN goto FAILURE END if (@subid_old IS NOT NULL) begin /* ** Delete old row for subscriber from MSmerge_replinfo. */ DELETE from MSmerge_replinfo where repid = @subid_old IF @@ERROR <> 0 BEGIN goto FAILURE END EXECUTE @retcode = dbo.sp_MSgenreplnickname @subid, @subnickname output if @@ERROR<>0 or @retcode<>0 goto FAILURE /* ** Add new row for subscriber to MSmerge_replinfo. */ INSERT INTO MSmerge_replinfo(repid, replnickname) values (@subid, @subnickname) IF @@ERROR <> 0 BEGIN goto FAILURE END end end else begin INSERT sysmergesubscriptions(subid, partnerid, datasource_type, datasource_path, srvid, db_name, pubid, status, subscriber_type, subscription_type, priority, sync_type, description, login_name, subscriber_server, publication, distributor) VALUES (@subid, @partnerid, 0, NULL, @subscriber_srvid, @subscriber_db, @pubid, @active, @subscriber_type, @subscription_type, @subscriber_priority, @sync_type, NULL, suser_sname(suser_sid()), @subscriber, @publication, @distributor) IF @@ERROR <> 0 BEGIN goto FAILURE END /* Look for existing nickname from any other subscription */ select @subnickname = max(replnickname) from MSmerge_replinfo, sysmergesubscriptions where repid = subid and srvid = @subscriber_srvid and db_name = @subscriber_db /* Generate a new replica nickname from the @subid */ if (@subnickname is null) begin EXECUTE @retcode = dbo.sp_MSgenreplnickname @subid, @subnickname output if @@ERROR<>0 or @retcode<>0 goto FAILURE end /* ** Add row for subscriber to MSmerge_replinfo. */ INSERT INTO MSmerge_replinfo(repid, replnickname) values (@subid, @subnickname) IF @@ERROR <> 0 BEGIN goto FAILURE END end COMMIT TRAN RETURN 0 FAILURE: /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION MSaddinitialsubscription COMMIT TRANSACTION end RAISERROR (14057, 16, -1) RETURN 1 go exec dbo.sp_MS_marksystemobject sp_MSaddinitialsubscription go grant exec on dbo.sp_MSaddinitialsubscription to public go raiserror('Creating procedure sp_MSmakearticleprocs', 0,1) GO create procedure sp_MSmakearticleprocs (@pubid uniqueidentifier, @artid uniqueidentifier) as declare @ownername sysname declare @objectname sysname declare @ins_procname sysname declare @sel_procname sysname declare @upd_procname sysname declare @guidstr nvarchar(40) declare @trigname sysname declare @objid int declare @dbname sysname declare @command nvarchar(1000) -- to be called after article is set up in a subscriber declare @retcode smallint /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) select @objid = max(objid) from sysmergearticles where artid = @artid -- get owner name, and table name select @objectname = name, @ownername = user_name(uid) from sysobjects where id = @objid -- get the insert and update proc names from sys articles select @ins_procname = insert_proc, @upd_procname = update_proc, @sel_procname = select_proc from sysmergearticles where pubid = @pubid and artid = @artid if object_id(@ins_procname) is not NULL begin exec ('drop proc ' + @ins_procname) if @@ERROR<>0 return (1) end if object_id(@upd_procname) is not NULL begin exec ('drop proc ' + @upd_procname) if @@ERROR<>0 return (1) end if object_id(@sel_procname) is not NULL begin exec ('drop proc ' + @sel_procname) if @@ERROR<>0 return (1) end -- create the procs set @dbname = db_name() /* If procedure already exists because article in multiple pubs don't bother */ if not exists (select * from sysobjects where name = @ins_procname and type = 'P') begin set @command = 'sp_MSmakeinsertproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @ins_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode <>0 return (1) exec @retcode = dbo.sp_MS_marksystemobject @ins_procname if @@ERROR<>0 OR @retcode <>0 return (1) exec ('grant exec on ' + @ins_procname + ' to public') if @@ERROR<>0 return (1) end /* If procedure already exists because article in multiple pubs don't bother */ if not exists (select * from sysobjects where name = @upd_procname and type = 'P') begin set @command = 'sp_MSmakeupdateproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @upd_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 OR @retcode <>0 return (1) exec @retcode = dbo.sp_MS_marksystemobject @upd_procname if @@ERROR<>0 or @retcode <>0 return (1) exec ('grant exec on ' + @upd_procname + ' to public') if @@ERROR<>0 return (1) end /* If procedure already exists because article in multiple pubs don't bother */ if not exists (select * from sysobjects where name = @sel_procname and type = 'P') begin set @command = 'sp_MSmakeselectproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @sel_procname + ', [' + convert(nchar(36), @pubid) + ']' exec @retcode = master..xp_execresultset @command, @dbname if @@ERROR<>0 or @retcode<>0 return (1) exec @retcode = dbo.sp_MS_marksystemobject @sel_procname if @@ERROR<>0 OR @retcode <>0 return (1) exec ('grant exec on ' + @sel_procname + ' to public') if @@ERROR<>0 return (1) end go exec dbo.sp_MS_marksystemobject sp_MSmakearticleprocs go grant exec on dbo.sp_MSmakearticleprocs to public go raiserror('Creating procedure sp_MSupdatesysmergearticles', 0,1) GO CREATE PROCEDURE sp_MSupdatesysmergearticles( @object sysname, /* Name of the table */ @artid uniqueidentifier, /* Article ID */ @owner sysname = NULL, @identity_support int = NULL, @next_seed bigint = NULL, @range bigint = NULL, @threshold int = NULL, @pubid uniqueidentifier = NULL ) AS declare @merge_pub_object_bit int declare @id int declare @qualified_name nvarchar(270) declare @colid int declare @colname sysname declare @mergepublish int SET NOCOUNT ON declare @retcode int declare @objid int if @owner is NULL or @owner = '' begin if exists (select name from sysobjects where id = object_id(@object)) select @owner = user_name(uid) from sysobjects where id = object_id(QUOTENAME(@object)) else begin raiserror(21078, 16, -1, @object) return (1) end end select @mergepublish = 0x4000 select @qualified_name = QUOTENAME(@owner) + '.' + QUOTENAME(@object) select @objid = object_id(@qualified_name) /* ** Check to see if current publication has permission */ /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) select @merge_pub_object_bit = 128 if (@artid is NULL) BEGIN RAISERROR (14057, 16, -1) RETURN (1) END begin tran save tran sp_MSupdatesysmergearticles if exists (select name from sysobjects where id = @objid) begin exec dbo.sp_replupdateschema @qualified_name if @@ERROR<>0 goto UNDO update sysobjects set replinfo = replinfo | @merge_pub_object_bit where id=@objid if @@ERROR<>0 goto UNDO update syscolumns set colstat=colstat | @mergepublish where id=@objid if @@ERROR<>0 goto UNDO /* ** update sysmergearticles.objid for all articles sharing the same base table */ update sysmergearticles set objid = @objid where artid = @artid IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto UNDO END /* ** update sysmergearticles.sysnc_objid for only the article in this publication ** and for articles that no longer has a valid sync_objid (usually for the ** non-filtered case) */ update sysmergearticles set sync_objid=@objid where artid = @artid and (pubid = @pubid or (object_name(sync_objid) is null and isnull(view_type,0) = 0)) IF @@ERROR <> 0 BEGIN RAISERROR (14057, 16, -1) goto UNDO END /* ** Set the next_seed, max value, and threshhold of identity table. New range and threshold are to be set ** by sp_addmergearticle if the table is to be republished. Message based subscribers have to look up the ** threshold and range info to see if they need to request a new range. Use max_identity for current_max for ** now. The value of current_max can change by sp_addmergearticle if republished. */ if @identity_support = 1 begin exec @retcode = sp_MSreseed @objid, @next_seed, @range if @@ERROR<>0 or @retcode<>0 GOTO UNDO insert MSrepl_identity_range (objid, max_identity, next_seed, current_max, range, threshold) values(@objid, @next_seed + @range, @next_seed, @next_seed + @range - 1, @range, @threshold) if @@ERROR<>0 begin GOTO UNDO end end end else -- THIS IS FINE. This dynamic query is there to provide a good error message. No need to use SP. raiserror(21078, 16, -1, @object) exec dbo.sp_replupdateschema @qualified_name if @@error<>0 goto UNDO commit tran RETURN 0 UNDO: rollback tran sp_MSupdatesysmergearticles commit tran RETURN 1 go exec dbo.sp_MS_marksystemobject sp_MSupdatesysmergearticles go grant exec on dbo.sp_MSupdatesysmergearticles to public go raiserror('Creating procedure sp_MSexclause', 0,1) GO create proc sp_MSexclause @tablenick int, @pubid uniqueidentifier as set nocount on declare @clause nvarchar(4000) declare @filterid int declare @joinnick int declare @jointable nvarchar(270) declare @table nvarchar(270) declare @basetable nvarchar(270) declare @filter_clause nvarchar(2000) declare @retcode int exec @retcode= sp_MStablenamefromnick @tablenick, @basetable out if @@error<>0 or @retcode<>0 return(1) select @table = QUOTENAME(name) from sysobjects where id in (select objid from sysmergearticles where nickname = @tablenick) declare f_c CURSOR LOCAL FAST_FORWARD for select art_nickname, join_filterclause from sysmergesubsetfilters where join_nickname = @tablenick and pubid = @pubid FOR READ ONLY open f_c fetch next from f_c into @joinnick, @filter_clause while (@@fetch_status <> -1) begin exec @retcode= sp_MStablenamefromnick @joinnick, @jointable out if @@error<>0 or @retcode<>0 goto Failure -- As helper proc for sp_MSmakeinsertproc, we can insert directly to the -- temp table as we build up more commands for the insert proc. -- Our commands are part of phase 8... set @clause = ' if @has_rows = 0 begin if exists (select 1 from ' + @basetable + ' (NOLOCK) , ' + @jointable + ' (NOLOCK) where ' insert into #tempcmd (phase, cmdtext) values (8, @clause) set @clause = @filter_clause insert into #tempcmd (phase, cmdtext) values (8, @clause) set @clause = ' and ' + @table + '.rowguidcol = @rowguid) select @has_rows = 1 end ' insert into #tempcmd (phase, cmdtext) values (8, @clause) fetch next from f_c into @joinnick, @filter_clause end close f_c deallocate f_c return(0) Failure: close f_c deallocate f_c return(1) go exec dbo.sp_MS_marksystemobject sp_MSexclause grant exec on dbo.sp_MSexclause to public go raiserror('Creating procedure sp_MSgetcolordinalfromcolname', 0,1) GO -- @colname should not be quoted when the following procedure is called create proc sp_MSgetcolordinalfromcolname @objid int, @sync_objid int, @colname sysname, @colordinal int OUTPUT as declare @colid int declare @objcolname sysname declare @iscomputed tinyint declare @xtype tinyint select @colid = min (colid) from syscolumns where id = @sync_objid and name not in (select name from syscolumns where id=@objid and (iscomputed=1 OR type_name(xtype)='timestamp')) select @objcolname = name from syscolumns where id = @sync_objid and colid = @colid select @iscomputed=iscomputed, @xtype = xtype from syscolumns where id = @objid and name = @colname set @colordinal = 1 while (@objcolname is not null and @objcolname <> @colname) begin if (@iscomputed=1 OR type_name(@xtype)='timestamp') goto Next_Column Next_Column: -- now set up to repeat the loop with the next column select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid set @objcolname = NULL if @colid is not null begin select @objcolname = name from syscolumns where id = @sync_objid and colid = @colid select @iscomputed=iscomputed, @xtype = xtype from syscolumns where id = @objid and name = @colname end set @colordinal = @colordinal + 1 end return 0 go exec dbo.sp_MS_marksystemobject sp_MSgetcolordinalfromcolname go grant exec on dbo.sp_MSgetcolordinalfromcolname to public go raiserror('Creating procedure sp_MSinsertbeforeimageclause', 0,1) GO create proc sp_MSinsertbeforeimageclause @pubid uniqueidentifier, @objid int, @tablenickstr nvarchar(12) as set nocount on declare @cmdpiece nvarchar(4000) declare @before_objid int declare @sync_objid int declare @before_name sysname declare @collist nvarchar(4000) declare @vallist nvarchar(4000) declare @colname sysname declare @colordinal smallint declare @argname sysname -- Do we have a before table? select @before_objid = max(before_image_objid) from sysmergearticles where objid = @objid and before_image_objid is not null select @before_name = OBJECT_NAME(@before_objid) select @sync_objid = sync_objid from sysmergearticles where objid=@objid and pubid=@pubid if @before_name is null begin return 0 end set @collist = '' -- Loop over columns to make the column list for the insert / select command declare col_cursor CURSOR LOCAL FAST_FORWARD for select name from syscolumns where id = @before_objid and name <> 'generation' and name <> 'system_delete' order by colid FOR READ ONLY open col_cursor set @vallist = '' fetch next from col_cursor into @colname while (@@fetch_status <> -1) begin --this column is not in vertical partitioning if not exists (select * from syscolumns where name=@colname and id=@sync_objid) begin fetch next from col_cursor into @colname continue end set @collist = @collist + QUOTENAME(@colname) + ', ' exec sp_MSgetcolordinalfromcolname @objid, @sync_objid, @colname, @colordinal out select @argname = '@p' + rtrim(convert(nchar, @colordinal)) set @vallist = @vallist + @argname + ', ' fetch next from col_cursor into @colname end close col_cursor deallocate col_cursor -- Our list has all of the columns except generation since that gets set to a local variable -- Make the insert command set @cmdpiece = ' declare @gen_cur int select @gen_cur = max(gen_cur) from sysmergearticles where nickname = ' + @tablenickstr insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) --select @cmdpiece set @cmdpiece = ' insert into ' + QUOTENAME(@before_name) + ' ( ' + @collist + ' generation, system_delete) values (' + @vallist + ' @gen_cur, 1 )' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) --select @cmdpiece return 0 go exec dbo.sp_MS_marksystemobject sp_MSinsertbeforeimageclause go grant exec on dbo.sp_MSinsertbeforeimageclause to public go raiserror('Creating procedure sp_MSmakeinsertproc', 0,1) GO -- This will be called by snapshot at publisher side and -- merge at the subscriber side, check for dbo permission create procedure sp_MSmakeinsertproc (@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier) as declare @argname sysname declare @id int declare @sync_objid int declare @qualified_name nvarchar(270) declare @idstr nvarchar(100) declare @iscomputed tinyint declare @xtype tinyint declare @colstat int declare @permissions int declare @permissions_str nvarchar(10) if @ownername is NULL or @ownername='' select @qualified_name = QUOTENAME(@tablename) else select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename) select @id = object_id(@qualified_name) if @id is NULL return (1) select @sync_objid = sync_objid, @permissions_str=convert(nvarchar(10),check_permissions), @permissions=check_permissions from sysmergearticles where objid=@id and pubid=@pubid set @idstr = rtrim(convert(nchar, @id)) declare @retcode int declare @colname nvarchar(140) declare @rgcolname nvarchar(140) declare @typename nvarchar(140) declare @colid smallint declare @status tinyint declare @len smallint declare @prec int declare @scale int declare @tablenick int declare @tablenickstr nvarchar(12) declare @colordinal smallint declare @cmdpiece nvarchar(4000) set nocount on -- Check for subscribing permission exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (0) execute @retcode = dbo.sp_MStablenickname @ownername, @tablename, @tablenick output IF @@ERROR <> 0 or @retcode <>0 return (0) set @tablenickstr = rtrim(convert(nchar, @tablenick)) -- create temp table to select the command text out of create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) -- insert text pieces that don't repeat for each column -- phase 0 : create procedure and fixed part of argument list set @cmdpiece = 'create procedure dbo.' + QUOTENAME(@procname) + ' (@rowguid uniqueidentifier, @generation int, @lineage varbinary(255), @colv varbinary(2048) ' insert into #tempcmd (phase, cmdtext) values (0, @cmdpiece) -- phase 1 is rest of argument list; goes in during loop over columns -- phase 2 : paren to close argument list, and variable declarations set @cmdpiece = ') as declare @tablenick int declare @errcode int declare @retcode int declare @has_rows int set @has_rows = 0 set nocount on set @errcode= 0 if sessionproperty(''replication_agent'') = 0 begin exec @retcode = dbo.sp_MSreplcheck_connection @objid = ' + @idstr + ' if @retcode<>0 or @@ERROR<>0 return (3) end ' if @permissions>0 begin select @cmdpiece = @cmdpiece + ' exec @retcode = dbo.sp_MSreplcheck_permission @objid = ' + @idstr + ', @type = 1, @permissions = ' + @permissions_str + ' if @retcode<>0 or @@ERROR<>0 return (4)' end select @cmdpiece = @cmdpiece + ' select @tablenick = ' + @tablenickstr insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece) -- phase 3 is optional set identity insert on, goes in during loop if needed -- phase 4 is beginning a sub transaction, setting save point and starting insert statement set @cmdpiece = ' begin transaction save transaction sp_insproc if @metadata_type = 1 or @metadata_type = 5 begin if not exists (select * from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid and lineage = @lineage_old) begin set @errcode= 2 goto Failure end end exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage, @colv, 2, @tombstone_rows_deleted = @has_rows OUTPUT if @retcode<>0 or @@ERROR<>0 begin set @errcode= 0 goto Failure end insert into ' + @qualified_name + ' (' insert into #tempcmd (phase, cmdtext) values (4, @cmdpiece) -- phase 5 is column list that we are inserting; done in loop -- phase 6 is just the opening and closing parens and VALUES keyword set @cmdpiece = ') values (' insert into #tempcmd (phase, cmdtext) values (6, @cmdpiece) -- phase 7 is all of those arguments as the list of value expressions; done in loop -- phase 8 finish insert, check status, etc. -- if we have a permanent view, check for case where we inserted a row that doesn't -- meet filters of subscriber we are getting the insert from set @cmdpiece = ') if (@@rowcount <> 1) begin set @errcode= 3 goto Failure end ' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) -- only add the following code when creating proc at publisher if exists (select 1 from sysmergepublications where pubid=@pubid and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()) begin -- Add in pieces that check for inserting a row that instantly means other rows need to be downloaded -- If we insert such a row, set the generation and partchangegen so that we will download everything -- that needs to go. exec sp_MSexclause @tablenick, @pubid if @@error<>0 return(1) if exists (select * from sysmergearticles where pubid = @pubid and objid = @id and view_type = 1) begin /* Get name of rowguidcol. Aliasing doesn't work through a view. */ select @rgcolname = QUOTENAME(name) from syscolumns where id = @id and ColumnProperty(@id, name, 'isrowguidcol') = 1 if @rgcolname is null set @rgcolname = 'rowguid' select @cmdpiece = ' if @has_rows = 0 begin if not exists (select 1 from ' + QUOTENAME(OBJECT_NAME(sync_objid)) from sysmergearticles where pubid = @pubid and objid = @id set @cmdpiece = @cmdpiece + ' where ' + @rgcolname + ' = @rowguid) begin select @has_rows = 1 ' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) exec sp_MSinsertbeforeimageclause @pubid, @id, @tablenickstr set @cmdpiece = ' end end ' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) end end -- if we already have a tombstone for this row, (especially a remove from partial) then -- make sure we will set the generation so that it goes on down to subscribers of republishers -- for backward compatibility, for 7.0 subscribers we do not want @has_rows to be 1 set @cmdpiece = ' if @has_rows > 0 update dbo.MSmerge_contents set generation = 0, partchangegen = 0 where rowguid = @rowguid and tablenick = @tablenick' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) set @cmdpiece = ' commit tran ' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) -- phase 9 is setting identity insert off if needed; done in loop -- phase 10 is returning our success / failure status set @cmdpiece = ' return(1) Failure: rollback tran sp_insproc commit tran return(@errcode) ' insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece) -- now loop over columns and insert missing command pieces select @colid = min (colid) from syscolumns where id = @sync_objid and name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp')) select @colname = QUOTENAME(name), @typename = type_name(xtype), @len = length, @prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale from syscolumns where id = @sync_objid and colid = @colid select @status = status, @iscomputed=iscomputed, @xtype=xtype, @colstat=colstat from syscolumns where id = @id and QUOTENAME(name) = @colname if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes select @len = @len/2 set @colordinal = 1 while (@colname is not null) begin if (@iscomputed=1 OR type_name(@xtype)='timestamp') goto Next_Column exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@error<>0 OR @retcode <>0 return (1) select @argname = '@p' + rtrim(convert(nchar, @colordinal)) -- add to argument list (phase 1) set @cmdpiece = ', ' + @argname + ' ' + @typename insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece) -- add to column list and value list if (@colordinal = 1) begin -- column list is phase 5 set @cmdpiece = @colname insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece) -- argname for values list is phase 7 set @cmdpiece = @argname insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece) end else begin -- column list is phase 5; need preceding comma since not the first one. set @cmdpiece = ', ' + @colname insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece) -- argname for values list is phase 7 need preceding comma since not the first one. set @cmdpiece = ', ' + @argname insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece) end -- is this an identity column without 'not for replication' marking? if (@status = 128) and (@colstat & 0x0008 =0) begin -- turning identity insert on is phase 3 set @cmdpiece = ' set identity_insert ' + @qualified_name + ' on' insert into #tempcmd (phase, cmdtext) values (3, @cmdpiece) -- turning identity insert on is phase 9 set @cmdpiece = ' set identity_insert ' + @qualified_name + ' off' insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece) end Next_Column: -- now set up to repeat the loop with the next column select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid set @colname = NULL if @colid is not null select @colname = QUOTENAME(name), @status = status, @typename = type_name(xtype), @len = length, @prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale from syscolumns where id = @sync_objid and colid = @colid select @status = status, @iscomputed=iscomputed, @xtype=xtype,@colstat=colstat from syscolumns where id = @id and QUOTENAME(name) = @colname if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes select @len = @len/2 set @colordinal = @colordinal + 1 end select @cmdpiece = ',@metadata_type tinyint = NULL, @lineage_old varbinary(255) = NULL' insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece) -- Now we select out the command text pieces in proper order so that our caller, -- xp_execresultset will execute the command that creates the stored procedure. select cmdtext from #tempcmd order by phase, step drop table #tempcmd go exec dbo.sp_MS_marksystemobject sp_MSmakeinsertproc go grant exec on dbo.sp_MSmakeinsertproc to public go raiserror('Creating procedure sp_MSmakeupdateproc', 0,1) GO -- This will be called by snapshot at publisher side and -- merge at the subscriber side, check for dbo permission create procedure sp_MSmakeupdateproc (@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier) as declare @retcode int declare @argname nvarchar(10) declare @varname nvarchar(10) declare @cmdpiece nvarchar(4000) declare @qualified_name nvarchar(270) declare @littlecomp nvarchar(300) declare @id int declare @sync_objid int declare @idstr nvarchar(100) declare @fast_multicol_updateproc_bit bit declare @permissions_str nvarchar(10) declare @permissions int set nocount on if @ownername is NULL or @ownername='' select @qualified_name = QUOTENAME(@tablename) else select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename) select @id = object_id(@qualified_name) if @id is NULL return (1) select @sync_objid = sync_objid, @fast_multicol_updateproc_bit = fast_multicol_updateproc, @permissions=check_permissions, @permissions_str=convert(nvarchar(10), check_permissions) from sysmergearticles where objid=@id and pubid = @pubid set @idstr = rtrim(convert(nchar, @id)) declare @colname nvarchar(140) declare @typename nvarchar(140) declare @colid smallint declare @colordinal smallint declare @colordstr nvarchar(4) declare @xtype tinyint declare @iscomputed tinyint declare @isrowguidcol tinyint declare @separate_update_needed tinyint declare @update_stmt_started tinyint declare @status tinyint declare @len smallint declare @blen smallint declare @prec int declare @scale int declare @tablenick int declare @tablenickstr nvarchar(12) declare @bytestr nvarchar(10) declare @byteordinal smallint declare @numbytes smallint declare @bitstr nvarchar(10) declare @colpat nvarchar(130) declare @has_updateable_columns_in_select_list bit select @separate_update_needed = 0 select @update_stmt_started = 0 select @has_updateable_columns_in_select_list = 0 /* ** Check for dbo permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) execute @retcode = dbo.sp_MStablenickname @ownername, @tablename, @tablenick output if @@ERROR <>0 OR @retcode <>0 return (1) set @tablenickstr = rtrim(convert(nchar, @tablenick)) -- create temp table to select the command text out of create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) -- insert text pieces that don't repeat for each column -- phase 0 : create procedure and fixed part of argument list set @cmdpiece = 'Create procedure dbo.' + @procname + ' (@rowguid uniqueidentifier, @setbm varbinary(125) = NULL, @metadata_type tinyint, @lineage_old varbinary(255), @generation int, @lineage_new varbinary(255), @colv varbinary(2048) ' insert into #tempcmd (phase, cmdtext) values (0, @cmdpiece) -- phase 1 is rest of argument list; goes in during loop over columns -- phase 2 paren to close argument list and fixed variable declarations set @cmdpiece = ') as declare @tablenick int declare @errcode int declare @fset int declare @match int declare @retcode smallint set nocount on if sessionproperty(''replication_agent'') = 0 begin exec @retcode = dbo.sp_MSreplcheck_connection @objid = ' + @idstr + ' if @retcode<>0 or @@ERROR<>0 return (3) end ' if @permissions>0 begin select @cmdpiece=@cmdpiece + ' exec @retcode = dbo.sp_MSreplcheck_permission @objid = ' + @idstr + ', @type=2, @permissions = ' + @permissions_str + ' if @retcode<>0 or @@ERROR<>0 return (4)' end select @cmdpiece = @cmdpiece + ' select @tablenick = ' + @tablenickstr insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece) -- phase 3 is rest of variable declarations; goes in during loop over columns -- phase 4 begin a transaction, set savepoint in case we roll back, begin select to get current values set @cmdpiece = ' begin transaction sub save transaction sub select ' insert into #tempcmd (phase, cmdtext) values (4, @cmdpiece) -- phase 5 is middle part of select assigning column values to local variables -- goes in loop -- phase 6 -- finish the select, check that metadata matches set @cmdpiece = ' from ' + @qualified_name + ' (updlock) where rowguidcol = @rowguid exec @retcode= dbo.sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output if @retcode<>0 or @@ERROR<>0 begin set @errcode= 3 goto Failure end if (@match = 1) begin ' insert into #tempcmd (phase, cmdtext) values (6, @cmdpiece) -- phase 7 is a bunch of if's that compare old values with new values ; goes in during loop -- phase 8 finish the stored procedure set @cmdpiece = ' exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, @colv, 2 if @retcode<>0 or @@ERROR<>0 begin set @errcode= 3 goto Failure end end else begin set @errcode= 2 goto Failure end commit transaction return(1) Failure: rollback transaction sub commit transaction return @errcode' insert into #tempcmd (phase, cmdtext) values (11, @cmdpiece) -- now do the loop over all columns and insert the missing pieces -- don't script out computed columns or timestamp columns select @colid = min (colid) from syscolumns where id = @sync_objid and name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp')) select @colname = NULL select @colname = name, @typename = type_name(xtype), @blen = length, @prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale from syscolumns where id = @sync_objid and colid = @colid select @status = status, @iscomputed=iscomputed, @xtype=xtype, @isrowguidcol = COLUMNPROPERTY(id, name, 'IsRowGuidCol') from syscolumns where id = @id and name = @colname -- get col count to determine what size our bitmask is going to be declare @colcount int select @colcount = count(*) from syscolumns where id = @sync_objid and name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp')) set @cmdpiece = ' declare @colbitmask binary(' + convert(nvarchar,1+(@colcount-1) / 8) + ') select @colbitmask = 0 ' select @numbytes = 1+(@colcount-1) / 8 insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece) if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes set @len = @blen/2 else set @len = @blen set @colordinal = 1 declare @firstCol tinyint set @firstCol= 1 while (@colname is not null) begin set @colordstr = convert(nvarchar(4), @colordinal) exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@ERROR <>0 OR @retcode <>0 return (1) if (@fast_multicol_updateproc_bit = 1) begin -- check if separate update statement is needed only if article supports fast multi-column updates. -- reset @separate_update_needed set @separate_update_needed = 0 --check if this column is part of the filter or join filter clause. -- if so, use a separater update statement for it rather than setting bitmask for the one cumulative update statement. set @colpat = '%' + @colname + '%' -- does updating this column change membership in a partial replica? if exists (select * from sysmergearticles where objid = @id and subset_filterclause like @colpat) set @separate_update_needed = 1 else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick and join_filterclause like @colpat) set @separate_update_needed = 1 else if exists (select * from sysmergesubsetfilters where join_nickname = @tablenick and join_filterclause like @colpat) set @separate_update_needed = 1 else if (@typename = 'ntext' or @typename = 'text' or @typename = 'image') set @separate_update_needed = 1 end else begin set @separate_update_needed = 1 -- separate update statement for each column. end if @status = 128 OR @iscomputed=1 OR type_name(@xtype)='timestamp' goto Next_Column set @colname = QUOTENAME(@colname) -- put in argument list element (phase 1) set @argname = '@p' + rtrim(@colordstr) set @cmdpiece = ', ' + @argname + ' ' + @typename + ' = NULL ' insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece) if (@isrowguidcol = 1) goto Next_Column if (@separate_update_needed = 0) begin select @bytestr = convert( nvarchar, 1 + (@colordinal-1) / 8 ) select @byteordinal = 1 + (@colordinal-1) / 8 select @bitstr = convert( nvarchar, power(2, (@colordinal-1) % 8 ) ) if (@update_stmt_started = 0) begin select @update_stmt_started = 1 select @cmdpiece = 'update ' + @qualified_name + ' set ' insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece) -- goes after phase 7 select @cmdpiece = ' where rowguidcol = @rowguid if (@@rowcount <> 1) begin set @errcode= 3 goto Failure end' insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece) -- goes after phase 9 which would be the SET clause for different columns end else begin select @cmdpiece = ',' insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece) end select @cmdpiece = @colname + ' = case substring(@colbitmask,' + @bytestr + ',1) & ' + @bitstr + ' when ' + @bitstr + ' then ' + @argname + ' else ' + @colname + ' end ' insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece) end -- put in declaration for variable (phase 3) -- text and image get no variable if (@typename <> 'ntext' and @typename <> 'text' and @typename <> 'image') begin set @varname = '@l' + rtrim(@colordstr) set @cmdpiece = 'declare ' + @varname + ' ' + @typename + ' ' insert into #tempcmd (phase, cmdtext) values (3, @cmdpiece) -- put in set piece to initialize variable to old value in select statement (phase 5) if @firstCol=1 begin set @cmdpiece= '' set @firstCol= 0 end else set @cmdpiece= ', ' set @cmdpiece = @cmdpiece + @varname + ' = ' + @colname insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece) -- put in if piece that compares old value with new, checks bit if argument is null if (@typename like '%char%') begin -- Compare binaries instead of variables so that case changes are caught as different set @littlecomp = 'convert(varbinary(' + rtrim(convert(nchar, @blen)) + '), ' + @argname + ') = convert(varbinary(' + rtrim(convert(nchar, @blen)) + '), ' + @varname + ')' end else begin set @littlecomp = @argname + ' = ' + @varname end set @cmdpiece = ' if ' + @littlecomp + ' set @fset = 0 else if ( ' + @varname + ' is null and ' + @argname + ' is null) set @fset = 0 else if ' + @argname + ' is not null set @fset = 1 else if @setbm = 0x0 set @fset = 0 else exec @fset = dbo.sp_MStestbit @setbm, ' + @colordstr + ' if @fset <> 0 begin ' insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece) if (@separate_update_needed = 0) begin if (@numbytes = 1) begin -- no bytes on left or right of the byte being bitwise OR-ed. set @cmdpiece = ' select @colbitmask = convert(varbinary(1), substring(@colbitmask, ' + @bytestr + ' , 1) | ' + @bitstr + ') end ' end else begin -- there could be byte(s) on left and/or right of byte being bitwise OR-ed. set @cmdpiece = ' select @colbitmask = convert ( varbinary(' + convert(nvarchar, @numbytes) + '), ' + case when (@byteordinal = 1) then '' else ' convert(varbinary(' + convert(nvarchar, @byteordinal-1) + '), substring(@colbitmask, 1, ' + convert(nvarchar, @byteordinal-1) + ')) +' end + ' convert(varbinary(1), substring(@colbitmask, ' + @bytestr + ' , 1) | ' + @bitstr + ') ' + case when (@byteordinal = @numbytes) then '' else ' + convert(varbinary(' + convert(nvarchar, @numbytes - @byteordinal) + '), substring(@colbitmask, ' + convert(nvarchar, @byteordinal+1) + ', ' + convert(nvarchar, @numbytes - @byteordinal) + ')) ' end + ' ) end ' end end else begin set @cmdpiece = ' update ' + @qualified_name + ' set ' + @colname + ' = ' + @argname + ' where rowguidcol = @rowguid if (@@rowcount <> 1) begin set @errcode= 3 goto Failure end end ' end insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece) select @has_updateable_columns_in_select_list = 1 end else begin -- for text and image, we just test if argument is null and whether bit is set -- build conditional update (phase 7) set @cmdpiece = ' if ' + @argname + ' is not null set @fset = 1 else if @setbm = 0x0 set @fset = 0 else exec @fset = dbo.sp_MStestbit @setbm, ' + @colordstr + ' if @fset <> 0 begin update ' + @qualified_name + ' set ' + @colname + ' = ' + @argname + ' where rowguidcol = @rowguid if (@@rowcount <> 1) begin set @errcode= 3 goto Failure end end ' -- Now insert the command to temp table insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece) end Next_Column: -- Advance loop to next column and repeat! select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid and name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp')) set @colname = NULL if (@colid is not null) begin select @colname = name, @typename = type_name(xtype), @blen = length, @prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale from syscolumns where id = @sync_objid and colid = @colid select @status = status, @iscomputed=iscomputed, @xtype=xtype, @isrowguidcol = COLUMNPROPERTY(id, name, 'IsRowGuidCol') from syscolumns where id = @id and name = @colname if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes set @len = @blen/2 else set @len = @blen set @colordinal = @colordinal + 1 end end -- Add dummy column list to select statement if there is no user updateable -- column. if @has_updateable_columns_in_select_list = 0 begin insert into #tempcmd (phase, cmdtext) values (3, N'declare @l int') insert into #tempcmd (phase, cmdtext) values (5, N'@l = 1') end -- Now we select out the command text pieces in proper order so that our caller, -- xp_execresultset will execute the command that creates the stored procedure. select cmdtext from #tempcmd order by phase, step drop table #tempcmd go exec dbo.sp_MS_marksystemobject sp_MSmakeupdateproc go grant exec on dbo.sp_MSmakeupdateproc to public go raiserror('Creating procedure sp_MSmakeselectproc', 0,1) GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON GO create procedure sp_MSmakeselectproc (@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier) as declare @retcode smallint declare @argname nvarchar(10) declare @varname nvarchar(10) declare @columns varbinary(128) declare @cmdpiece nvarchar(4000) declare @qualified_name nvarchar(270) declare @column_list table (line_no int identity(1,1) primary key, line nvarchar(4000)) declare @littlecomp nvarchar(300) declare @colid int declare @col_name nvarchar(140) declare @id int declare @idstr nvarchar(100) declare @sync_objid int set nocount on if @ownername is NULL or @ownername='' select @qualified_name = QUOTENAME(@tablename) else select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename) select @id = object_id(@qualified_name) select @sync_objid=sync_objid from sysmergearticles where objid = @id and pubid=@pubid set @idstr = rtrim(convert(nchar, @id)) /* ** Include computed columns. */ IF EXISTS (select name from syscolumns where id = @id and (name not in (select name from syscolumns where id = @sync_objid))) OR exists (select name from syscolumns where id=@id and iscomputed=1) OR ObjectProperty(@id, 'TableHasTimestamp') = 1 BEGIN DECLARE column_cursor CURSOR LOCAL FAST_FORWARD FOR select name from syscolumns where id=@id and iscomputed<>1 and type_name(xtype) <>'timestamp' and name in (select name from syscolumns where id=@sync_objid) FOR READ ONLY open column_cursor fetch next from column_cursor into @col_name WHILE (@@fetch_status <> -1) BEGIN if ColumnProperty(@id, @col_name, 'isrowguidcol') = 1 select @col_name='rowguidcol' else set @col_name = QUOTENAME(@col_name) if (select count(*) from @column_list) = 0 insert into @column_list(line) values (@col_name + ' ') else insert into @column_list(line) values (', ' + @col_name + ' ') fetch next from column_cursor into @col_name END close column_cursor deallocate column_cursor if (select count(*) from @column_list) = 0 begin RAISERROR(21125, 16, -1) return (1) end END else insert into @column_list(line) values('t.*') /* ** Check for dbo permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) set @cmdpiece= 'SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON' exec (@cmdpiece) if @@error<>0 return(1) -- create temp table to select the command text out of create table #tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) select @cmdpiece = ' Create procedure dbo.' + QUOTENAME(@procname) + ' (@type int output, @rowguid uniqueidentifier=NULL) AS declare @retcode int set nocount on if sessionproperty(''replication_agent'') = 0 begin exec @retcode = dbo.sp_MSreplcheck_connection @objid= ' + @idstr + ' if @@ERROR<>0 or @retcode<>0 return (1) end if @type = 1 begin select ' insert into #tempcmd (cmdtext) values (@cmdpiece) insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1 insert into #tempcmd (cmdtext) select ' ' + line from @column_list where line_no > 1 order by line_no asc select @cmdpiece=' from ' + @qualified_name + ' t where rowguidcol = @rowguid if @@ERROR<>0 return(1) end else if @type < 4 begin select c.tablenick, c.rowguid, c.generation, c.lineage, c.colv1 ,' insert into #tempcmd (cmdtext) values (@cmdpiece) insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1 insert into #tempcmd (cmdtext) select ' ' + line from @column_list where line_no > 1 order by line_no asc select @cmdpiece=' from ' + @qualified_name + ' t, #cont c where t.rowguidcol = c.rowguid order by t.rowguidcol if @@ERROR<>0 return(1) end else if @type = 4 begin set @type = 0 if exists (select * from ' + @qualified_name + ' where rowguidcol = @rowguid) set @type = 3 if @@ERROR<>0 return(1) end else if @type = 5 begin delete ' + @qualified_name + ' where rowguidcol = @rowguid if @@ERROR<>0 return(1) end else if @type = 6 -- sp_MSenumcolumns begin select ' insert into #tempcmd (cmdtext) values (@cmdpiece) insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1 insert into #tempcmd (cmdtext) select ' ' + line from @column_list where line_no > 1 order by line_no asc select @cmdpiece=' from ' + @qualified_name + ' t where 1=2 if @@ERROR<>0 return(1) end else if @type = 7 -- sp_MSlocktable begin select 1 from ' + @qualified_name + '(tablock holdlock) where 1 = 2 if @@ERROR<>0 return(1) end else if @type = 8 -- put update lock begin if not exists (select * from ' + @qualified_name + '(UPDLOCK HOLDLOCK) where rowguidcol = @rowguid) begin RAISERROR(20031 , 16, -1) return(1) end end return(0)' insert into #tempcmd (cmdtext) values (@cmdpiece) select cmdtext from #tempcmd order by step drop table #tempcmd go exec dbo.sp_MS_marksystemobject sp_MSmakeselectproc go grant exec on dbo.sp_MSmakeselectproc to public go SET ANSI_NULLS OFF go raiserror('Creating procedure sp_MSdropconstraints', 0,1) GO -- This will be called merge at the subscriber side, check for dbo permission create procedure sp_MSdropconstraints @table sysname as declare @const_name nvarchar(140) declare @objid int declare @retcode int /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) select @table=QUOTENAME(@table) set @objid = object_id(@table) if @objid is null select @objid = id from sysobjects where name=@table if @objid is NULL return (1) select @const_name = QUOTENAME(object_name(constid)) from sysreferences where fkeyid = @objid while @const_name is not null begin exec ('alter table ' + @table + ' drop constraint ' + @const_name) if @@ERROR <> 0 return (1) set @const_name = NULL select @const_name = QUOTENAME(object_name(constid)) from sysreferences where fkeyid = @objid end return (0) go exec dbo.sp_MS_marksystemobject sp_MSdropconstraints go grant exec on dbo.sp_MSdropconstraints to public go raiserror('Creating procedure sp_MSinsertschemachange', 0,1) GO CREATE PROCEDURE sp_MSinsertschemachange( @pubid uniqueidentifier, @artid uniqueidentifier = NULL, /* Can be NULL for directory commands */ @schemaversion int, @schemaguid uniqueidentifier, @schematype int, @schematext nvarchar(2000) ) as declare @retcode int declare @constraintname sysname declare @owner sysname declare @tablename sysname declare @qualname nvarchar(270) declare @objid int declare @alter_table_type int /* ** Check for subscribing permission */ exec @retcode=sp_MSreplcheck_subscribe if @retcode<>0 or @@ERROR<>0 return (1) select @alter_table_type = 11 /* Parameter validation */ if (@schemaversion is null) begin RAISERROR(14043, 16, -1, '@schemaversion') return (1) end if (not exists(select * from sysobjects where name = 'MSmerge_contents')) begin RAISERROR(20054 , 16, -1) return (1) end /* ** Special case: for push subscription, reinit-with-upload, we do not want to cleanup subscriber side ** cause we need to upload; however we still need to apply alter-table. To avoid duplicate key insert ** failure, we no-OP this insert of alter-table schema. */ if @schematype=@alter_table_type and exists (select * from sysmergeschemachange where pubid=@pubid and schemaversion=@schemaversion) return (1) insert into sysmergeschemachange with (HOLDLOCK TABLOCKX) (pubid, artid, schemaversion, schemaguid, schematype, schematext) values (@pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext) if @@error <> 0 begin RAISERROR(21305 , 16, -1) return (1) end /* update the schema version and schemaguid in MSmerge_replinfo */ declare @my_nickname int declare @srvid int select @srvid = 0 declare @repid uniqueidentifier select @my_nickname = replnickname, @repid = repid from MSmerge_replinfo where repid in (select subid from sysmergesubscriptions where srvid = @srvid and db_name = DB_NAME() and pubid = @pubid) update MSmerge_replinfo set schemaversion = @schemaversion, schemaguid = @schemaguid where repid = @repid and (schemaversion<@schemaversion or schemaversion is NULL) if @@error <> 0 begin RAISERROR(21305 , 16, -1) return (1) end /* only delete the publisher side check constraint at subscriber side */ if @schematype=15 and not exists (select * from sysmergepublications where pubid=@pubid and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()) begin select @objid=objid from sysmergearticles where artid=@artid and pubid=@pubid select @owner = user_name(uid) from sysobjects where id = @objid select @tablename = object_name(@objid) select @qualname = QUOTENAME(@owner) + '.' + QUOTENAME(@tablename) /* ** This is to removed CHECK constraint we accidentally propagated to subscriber side on identity check */ select @constraintname = 'repl_identity_range_pub_' + convert(nvarchar(36), @artid) select @constraintname = REPLACE(@constraintname, '-', '_') if exists (select * from sysobjects where name = @constraintname and xtype='C') begin exec ('alter table '+ @qualname + ' drop constraint ' + @constraintname) if @@ERROR<>0 return (1) end select @constraintname = 'repl_identity_range_repub_' + convert(nvarchar(36), @artid) select @constraintname = REPLACE(@constraintname, '-', '_') if exists (select * from sysobjects where name = @constraintname and xtype='C') begin exec ('alter table '+ @qualname + ' drop constraint ' + @constraintname) if @@ERROR<>0 return (1) end end return (0) go exec dbo.sp_MS_marksystemobject sp_MSinsertschemachange go grant exec on dbo.sp_MSinsertschemachange to public go raiserror('Creating procedure sp_MSgetviewcolumnlist', 0, 1) GO create procedure sp_MSgetviewcolumnlist( @pubid uniqueidentifier, @source_objid int, @column_list nvarchar(4000) OUTPUT ) AS /* ** if it is not vertically partitioned, then get all columns ** else get the column list as given in columns of sysmergearticles */ declare @bitset int declare @columns varbinary(125) declare @setcolcnt int declare @colcnt int declare @colname nvarchar(140) declare @colid int select @columns = columns from sysmergearticles where objid = @source_objid and pubid=@pubid select @column_list = '' select @setcolcnt = 0 select @colcnt = count(*) from syscolumns where id = @source_objid declare collist CURSOR LOCAL FAST_FORWARD FOR select name, colid from syscolumns where id = @source_objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid ASC FOR READ ONLY open collist fetch collist into @colname, @colid WHILE (@@fetch_status <> -1) BEGIN set @bitset = 1 if @columns is NOT NULL exec @bitset = sp_MStestbit @columns, @colid if @bitset<>0 begin select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid' if @column_list='' --which can cause problems and is not necessary. select @column_list = @colname else select @column_list = @column_list + ', ' + @colname select @setcolcnt = @setcolcnt + 1 end fetch next from collist into @colname, @colid END close collist deallocate collist if @setcolcnt = @colcnt select @column_list = ' * ' return 0 GO exec dbo.sp_MS_marksystemobject sp_MSgetviewcolumnlist go grant exec on dbo.sp_MSgetviewcolumnlist to public go raiserror('Creating procedure sp_MSvalidatearticle', 0, 1) GO create procedure sp_MSvalidatearticle @artid uniqueidentifier, @pubid uniqueidentifier, @expected_rowcount bigint = NULL OUTPUT, @expected_checksum numeric = NULL OUTPUT, @validation_type int = NULL, @full_or_fast tinyint = 2 as -- get name of sync object and owner declare @objid int declare @syncobjid int declare @owner sysname declare @object sysname declare @view_type tinyint declare @temp_view tinyint declare @retcode int declare @rowcount_only int declare @source_objid int declare @column_list nvarchar(4000) /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end /* The @rowcount_only param is overloaded for shiloh release due to backward compatibility concerns. In shiloh, the checksum functionality has changed. So 7.0 subscribers will have the old checksum routines, which generate different CRC values, and do not have functionality for vertical partitions, or logical table structures where column offsets differ (due to ALTER TABLEs that DROP and ADD columns). In 7.0, this was a bit column. 0 meant do not do just a rowcount - do a checksum. 1 meant just do a rowcount. For Shiloh, this parameter is changed to a smallint with these options: 0 - Do a 7.0 compatible checksum 1 - Do a rowcount check only 2 - Use new Shiloh checksum functionality. Note that because 7.0 subscribers will take this parameter as a bit type, not a smallint, it will be interpreted as simply ON. That means that passing a 2, and having a 7.0 subscriber, will result in the 7.0 subscriber doing only rowcount validation. The Shiloh subscribers will do both rowcount and checksum. If you want 7.0 subscribers to do checksum validation, use the value of 0 for this parameter. Shiloh subscribers can do the 7.0 compatible checksum, but that checksum has the same 7.0 limitations for vertical partitions and differences in physical table structure.) */ if @validation_type = 3 set @rowcount_only = 2 else if @validation_type = 2 set @rowcount_only = 0 else set @rowcount_only = 1 select @syncobjid = sync_objid, @objid = objid, @view_type = view_type from sysmergearticles where pubid = @pubid and artid = @artid select @source_objid = @objid if (@syncobjid is not null and @syncobjid <> 0) set @objid = @syncobjid select @object = name, @owner = user_name(uid) from sysobjects where id = @objid -- if sync object is a temp view, we can't do this... set @temp_view = 2 if @view_type = @temp_view begin RAISERROR (20069, 16, -1) return 1 end else if (@view_type = 1 and @rowcount_only = 0) begin RAISERROR (21017, 16, -1) return 1 end exec @retcode = sp_MSgetviewcolumnlist @pubid, @source_objid, @column_list OUTPUT if @@ERROR<>0 OR @retcode <> 0 return @retcode -- call sp_table_validation exec @retcode = dbo.sp_table_validation @object, @expected_rowcount OUTPUT, @expected_checksum OUTPUT, @rowcount_only, @owner, @full_or_fast, 0, NULL, @column_list if @@error<>0 return(1) return @retcode GO exec dbo.sp_MS_marksystemobject sp_MSvalidatearticle go grant exec on dbo.sp_MSvalidatearticle to public go raiserror('Creating procedure sp_MSsubscriptionvalidated', 0, 1) GO create procedure sp_MSsubscriptionvalidated @subid uniqueidentifier, @pubid uniqueidentifier, @log_attempt bit = 0 as declare @now datetime declare @retcode int select @now=getdate() if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @pubid = @pubid if @retcode<>0 or @@ERROR<>0 return (1) end if @log_attempt=0 update sysmergesubscriptions set last_validated = @now, attempted_validate=@now where subid = @subid and pubid = @pubid else update sysmergesubscriptions set attempted_validate = @now where subid = @subid and pubid = @pubid if @@rowcount <> 1 or @@error <> 0 begin RAISERROR (20070, 16, -1) return (1) end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSsubscriptionvalidated go grant exec on dbo.sp_MSsubscriptionvalidated to public raiserror('Creating procedure sp_MSdroparticletombstones', 0, 1) GO create procedure sp_MSdroparticletombstones @artid uniqueidentifier as declare @tablenick int select @tablenick = nickname from sysmergearticles where artid = @artid if @tablenick is not null begin delete from dbo.MSmerge_tombstone where tablenick = @tablenick delete from dbo.MSmerge_contents where tablenick = @tablenick delete from dbo.MSmerge_genhistory where art_nick=@tablenick end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSdroparticletombstones go grant exec on dbo.sp_MSdroparticletombstones to public raiserror('Creating procedure sp_MSproxiedmetadata', 0, 1) GO create procedure sp_MSproxiedmetadata @tablenick int, @rowguid uniqueidentifier, @lineage varbinary(256), @colv varbinary(2048) as declare @old_lin varbinary(256) declare @old_colv varbinary(2048) declare @retcode int Loop: set @old_lin= NULL select @old_lin = lineage, @old_colv = colv1 from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid if (@old_lin IS NOT NULL) begin exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, @old_colv if @@error<>0 or @retcode<>0 begin return(1) end update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin if @@rowcount = 0 goto Loop end else begin select @old_lin = lineage from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid if (@old_lin IS NULL) return (0) exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, NULL if @@error<>0 or @retcode<>0 begin return(1) end update dbo.MSmerge_tombstone set lineage = @lineage where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin if @@rowcount = 0 goto Loop end return (0) GO exec dbo.sp_MS_marksystemobject sp_MSproxiedmetadata grant exec on dbo.sp_MSproxiedmetadata to public go raiserror('Creating procedure sp_MScontractsubsnb', 0,1) GO create PROCEDURE sp_MScontractsubsnb (@pubid uniqueidentifier, @tablenick int, @basetable nvarchar(270)) AS declare @filter_clause nvarchar(4000) declare @join_nick int declare @jointable nvarchar(270) declare @filterid int declare @retcode int declare @tablenickstr nvarchar(10) set @tablenickstr = convert(nchar(10), @tablenick) /* First, try to remove rows from notbelong based on the article filter, if there is one */ select @filter_clause = subset_filterclause from sysmergearticles where pubid = @pubid and nickname = @tablenick if len(@filter_clause) > 0 begin exec ('delete from #notbelong with (paglock) where tablenick = ' + @tablenickstr + ' and rowguid in (select RowGuidCol from ' + @basetable + ' where ' + @filter_clause + ')' ) if @@error<>0 return(1) end /* Now loop over any join filters that have this as the base_table */ select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick while @filterid is not null begin /* Get joining table and filter clause */ select @join_nick = join_nickname, @filter_clause = join_filterclause from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @jointable out, @pubid if @@error<>0 or @retcode<>0 return(1) /* Exec query to remove rows from #notbelong that still belong to partial */ exec ('delete from #notbelong with (paglock) where tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @basetable + '.RowGuidCol from ' + @basetable + ', ' + @jointable + ' where (' + @filter_clause + ') and ' + @jointable + '.RowGuidCol not in (select rowguid from #notbelong))') if @@error<>0 return(1) /* Find the next filter that might apply */ select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick and join_filterid > @filterid end return(0) go exec dbo.sp_MS_marksystemobject sp_MScontractsubsnb go grant exec on dbo.sp_MScontractsubsnb to public go raiserror('Creating procedure sp_MSexpandsubsnb', 0,1) GO create PROCEDURE sp_MSexpandsubsnb (@pubid uniqueidentifier) AS declare @filterid int declare @base_nick int declare @join_nick int declare @basetable nvarchar(270) declare @jointable nvarchar(270) declare @join_clause nvarchar(4000) declare @retcode int declare @error int declare @rowcount int declare @base_nickstr nvarchar(10) declare @join_unique_key int /* get first filter to expand on */ select @filterid = min(join_filterid) from sysmergesubsetfilters f, #notbelong nb where pubid = @pubid and nb.tablenick = f.join_nickname and f.join_filterid > nb.flag while @filterid is not null begin /* get join clause and tables for this filter */ select @join_nick = join_nickname, @join_clause = join_filterclause, @base_nick = art_nickname, @join_unique_key = join_unique_key from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @jointable out, @pubid if @@error<>0 or @retcode<>0 return(1) exec @retcode = dbo.sp_MStablenamefromnick @base_nick, @basetable out, @pubid if @@error<>0 or @retcode<>0 return(1) set @base_nickstr = convert(nchar(10), @base_nick) /* Mark rows so that we know we've expanded those rows for this filter */ update #notbelong set flag = @filterid where flag < @filterid /* exec an insert/select query to expand #notbelong */ exec ('insert into #notbelong (tablenick, rowguid, flag) select distinct ' + @base_nickstr + ', ' + @basetable + '.RowGuidCol, 0 from ' + @basetable + ', ' + @jointable + ' where ( ' + @jointable + '.RowGuidCol in (select rowguid from #notbelong) ) and (' + @join_clause + ')') select @error=@@error, @rowcount=@@rowcount if @error<>0 return(1) /* ** if any rows inserted, try to contract the #notbelong table. ** Only join filters that are non unique need to contract the ** NOTBELONGS table - */ if @rowcount <> 0 and @join_unique_key = 0 begin exec @retcode = dbo.sp_MScontractsubsnb @pubid, @base_nick, @basetable if @@error<>0 or @retcode<>0 return(1) end /* get next filter to expand with */ select @filterid = min(join_filterid) from sysmergesubsetfilters f, #notbelong nb where pubid = @pubid and nb.tablenick = f.join_nickname and f.join_filterid > nb.flag end return(0) go exec dbo.sp_MS_marksystemobject sp_MSexpandsubsnb go grant exec on dbo.sp_MSexpandsubsnb to public go raiserror('Creating procedure sp_MSdelsubrows', 0,1) GO create PROCEDURE sp_MSdelsubrows (@rowguid uniqueidentifier, @tablenick int, @metadata_type tinyint, /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */ @lineage_old varbinary(255), @generation int, @lineage_new varbinary(255), @pubid uniqueidentifier = NULL, @rowsdeleted INT = NULL OUTPUT) as declare @match int declare @errcode int declare @new_metatype tinyint declare @retcode smallint declare @reason nvarchar(255) declare @procname sysname declare @tnstr nvarchar(11) declare @error int, @rowcount int /* By default this sp should delete exactly one row */ set @rowsdeleted = 1 /* ** Check to see if current publication has permission */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (0) end /* Parameter validation */ if (@rowguid is null) begin RAISERROR(14043, 16, -1, '@rowguid') return (0) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick') return (0) end if (@lineage_new is null) begin RAISERROR(14043, 16, -1, '@lineage_new') return (0) end -- Are we just changing the type of a tombstone? if (@metadata_type = 5 or @metadata_type = 1) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin if (@metadata_type = 5) set @reason = formatmessage (20563) -- Remove from partial else if (@metadata_type = 1) set @reason = formatmessage (20562) -- User delete update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason, generation = @generation, lineage = @lineage_new where rowguid = @rowguid and tablenick = @tablenick return 1 end end -- Are we just changing the type of a tombstone? else if (@metadata_type = 6) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin set @reason = formatmessage (20564) -- System deleted update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason where rowguid = @rowguid and tablenick = @tablenick return 1 end end -- begin transaction and lock row that we plan to delete begin transaction save tran sp_MSdelsubrows select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid exec @retcode = @procname @type =8, @rowguid=@rowguid IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end if @metadata_type = 5 begin set @match = 1 set @new_metatype = 5 end else if @metadata_type = 6 begin set @match = 1 set @new_metatype = 6 end else if @metadata_type = 7 begin set @match = 1 set @new_metatype = 7 end else begin exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end set @new_metatype = 1 end if (@match = 1) begin /* If there are any joinfilters with this as the join table, try to expand to deleting ** a set of related rows. */ if (exists (select * from sysmergesubsetfilters where pubid = @pubid and join_nickname = @tablenick)) begin declare @tn int declare @table_name nvarchar(270) select @rowsdeleted = 0 set @reason = formatmessage (20563) -- Moved out of partial range /* create temp and put in our tablenick, rowguid */ create table #notbelong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL) create index #indnbelong on #notbelong (tablenick, rowguid) insert into #notbelong (tablenick, rowguid, flag) values (@tablenick, @rowguid, 0) /* call expand proc */ exec @retcode = dbo.sp_MSexpandsubsnb @pubid IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end select @tn = max(tablenick) from #notbelong where flag > -1 while @tn is not null begin select @tnstr = convert(nvarchar(11), @tn) exec @retcode = dbo.sp_MStablenamefromnick @tn, @table_name out, @pubid -- delete all rows indicated by the temp table IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end exec ('delete from ' + @table_name + ' where RowGuidCol in (select rowguid from #notbelong where tablenick = ' + @tnstr + ')' ) select @error=@@error, @rowcount=@@rowcount IF @error<>0 begin set @errcode= 0 goto Failure end select @rowsdeleted = @rowsdeleted + @rowcount -- move on to next nickname - decreasing makes delete order correct update #notbelong set flag = -1 where tablenick = @tn select @tn = max(tablenick) from #notbelong where flag > -1 end -- change tombstone type for those rows update dbo.MSmerge_tombstone set type = 5, reason = @reason from #notbelong nb, dbo.MSmerge_tombstone ts where ts.tablenick = nb.tablenick and ts.rowguid = nb.rowguid drop table #notbelong exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end end else begin -- select_proc makes a delete with @type = 5, despite its name. exec @retcode = @procname @type =5, @rowguid=@rowguid select @error= @@error, @rowcount= @@rowcount IF @error<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end if (@rowcount = 1) begin exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end end else goto Failure end end else begin set @errcode= 2 goto Failure end commit tran return 1 -- in sp_MSdelsubrows, 1=okay Failure: rollback tran sp_MSdelsubrows commit tran return(@errcode) -- in sp_MSdelsubrows, 0=error go exec dbo.sp_MS_marksystemobject sp_MSdelsubrows go grant exec on dbo.sp_MSdelsubrows to public go raiserror('Creating procedure sp_MSdelsubrowsbatch', 0,1) GO create PROCEDURE sp_MSdelsubrowsbatch (@tablenick int, @rowguid_array varbinary(8000), @metadatatype_array varbinary(500), /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */ @oldlineage_len_array varbinary(1000), -- -- specifies the number of lineages in the @oldlineage_array. @oldlineage_array image, @generation_array varbinary(2000), @newlineage_len_array varbinary(1000), -- specifies the number of lineages in the @newlineage_array. @newlineage_array image, @pubid uniqueidentifier = NULL, @rowsdeleted INT = NULL OUTPUT) as declare @rowguid uniqueidentifier, @metadata_type tinyint, @lineage_old varbinary(255), @generation int, @lineage_new varbinary(255), @match int, @errcode int, @new_metatype tinyint, @retcode smallint, @reason nvarchar(255), @procname sysname, @tnstr nvarchar(11), @error int, @rowcount int, @tablenicklast int, @rowguidarraylen int, @oldlineage_len smallint, @newlineage_len smallint, @guidoffset int, @metatypeoffset int, @oldlinlenoffset int, @newlinlenoffset int, @oldlinoffset int, @newlinoffset int, @genoffset int, @transaction_started bit declare @rowstochangetype TABLE (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL) -- Do all DDL first create table #notbelong ( tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL, generation int NULL, lineage_old varbinary(255) NULL, metadatatype_old tinyint NULL, lineage_new varbinary(255) NULL, metadatatype_new tinyint NULL, original_row bit NULL default 0) create index #indnbelong on #notbelong (tablenick, rowguid) /* ** Check to see if current publication has permission. Skip check if caller is the merge agent. */ if sessionproperty('replication_agent') = 0 begin exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick if @retcode<>0 or @@ERROR<>0 return (0) end /* Parameter validation */ if (@rowguid_array is null) begin RAISERROR(14043, 16, -1, '@rowguid_array') return (0) end if (@tablenick is null) begin RAISERROR(14043, 16, -1, '@tablenick_array') return (0) end if (@newlineage_array is null) begin RAISERROR(14043, 16, -1, '@newlineage_array') return (0) end set @rowsdeleted = 0 set @transaction_started = 0 -- initialize offsets and length for walking through arrays set @guidoffset = 1 set @metatypeoffset = 1 set @oldlinlenoffset = 1 set @newlinlenoffset = 1 set @oldlinoffset = 1 set @newlinoffset = 1 set @genoffset = 1 set @rowguidarraylen = datalength(@rowguid_array) -- walk through arrays and populate temp table while (@guidoffset < @rowguidarraylen) begin set @rowguid = substring(@rowguid_array, @guidoffset, 16) -- 16 = sizeof uniqueidentifier (rowguid) set @metadata_type = substring(@metadatatype_array, @metatypeoffset, 1) -- 1 = sizeof tinyint (metadata_type) set @generation = substring(@generation_array, @genoffset, 4) -- 4 = sizeof int (generation) set @oldlineage_len = substring(@oldlineage_len_array, @oldlinlenoffset, 2) -- 2 = sizeof smallint (oldlineage_len) set @newlineage_len = substring(@newlineage_len_array, @newlinlenoffset, 2) -- 2 = sizeof smallint (newlineage_len) set @lineage_old = substring(@oldlineage_array, @oldlinoffset, @oldlineage_len) -- @oldlineage_len = sizeof old lineage for current row set @lineage_new = substring(@newlineage_array, @newlinoffset, @newlineage_len) -- @newlineage_len = sizeof new lineage for current row -- increment offsets for next row set @guidoffset = @guidoffset + 16 set @metatypeoffset = @metatypeoffset + 1 set @genoffset = @genoffset + 4 set @oldlinlenoffset = @oldlinlenoffset + 2 set @newlinlenoffset = @newlinlenoffset + 2 set @oldlinoffset = @oldlinoffset + @oldlineage_len set @newlinoffset = @newlinoffset + @newlineage_len -- Insert the old metadata type as the new metadata type. We can modify later if it is supposed to be different. insert into #notbelong (tablenick, rowguid, flag, generation, lineage_old, metadatatype_old, lineage_new, metadatatype_new, original_row) values (@tablenick, @rowguid, 0, @generation, @lineage_old, @metadata_type, @lineage_new, @metadata_type, 1) end if (exists(select * from #notbelong)) begin declare @tn int declare @table_name nvarchar(270) -- Expansion is an expensive and time-consuming process. Defer starting transaction until after expansion -- has taken place. /* call expand proc */ exec @retcode = dbo.sp_MSexpandsubsnb @pubid IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid -- After the #notbelong has been expanded, the original_row column can be used to distinguish original rows -- from the rows that were brought in by the expansion. -- open a cursor on #notbelong for rows with original_row = 1 declare original_rows_1 CURSOR LOCAL FAST_FORWARD for select rowguid, generation, lineage_old, metadatatype_old, lineage_new from #notbelong where tablenick = @tablenick and original_row = 1 FOR READ ONLY open original_rows_1 fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new begin transaction save tran start_of_batch set @transaction_started = 1 while (@@fetch_status <> -1) begin -- Are we just changing the type of a tombstone? if (@metadata_type = 5 or @metadata_type = 1) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin if (@metadata_type = 5) set @reason = formatmessage (20563) -- Remove from partial else if (@metadata_type = 1) set @reason = formatmessage (20562) -- User delete update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason, generation = @generation, lineage = @lineage_new where rowguid = @rowguid and tablenick = @tablenick -- This row will later be removed from #notbelong. We were only supposed to update the tombstone -- metadata type for this row (which we have already done above). insert into @rowstochangetype values (@tablenick, @rowguid) fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new continue -- on to the next row end end -- Are we just changing the type of a tombstone? else if (@metadata_type = 6) begin if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick) begin set @reason = formatmessage (20564) -- System deleted update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason where rowguid = @rowguid and tablenick = @tablenick -- This row will later be removed from #notbelong. We were only supposed to update the tombstone -- metadata type for this row (which we have already done above). insert into @rowstochangetype values (@tablenick, @rowguid) fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new continue -- on to the next row end end -- lock this particular row of the base table exec @retcode = @procname @type = 8, @rowguid=@rowguid IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 close original_rows_1 deallocate original_rows_1 goto Failure end if @metadata_type = 5 begin set @match = 1 set @new_metatype = 5 end else if @metadata_type = 6 begin set @match = 1 set @new_metatype = 6 end else if @metadata_type = 7 begin set @match = 1 set @new_metatype = 7 end else begin exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 close original_rows_1 deallocate original_rows_1 goto Failure end set @new_metatype = 1 end if (@match = 1) begin if (@metadata_type <> @new_metatype) begin -- we will later need this new metadata type when calling sp_MSsetrowmetadata update #notbelong set metadatatype_new = @new_metatype where tablenick = @tablenick and rowguid = @rowguid and original_row = 1 end end else begin set @errcode= 2 close original_rows_1 deallocate original_rows_1 goto Failure end fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new end close original_rows_1 deallocate original_rows_1 -- delete the rows in #notbelong that needn't be deleted. We were only supposed to update the tombstone -- metadata type for those rows (which we have already done above). delete #notbelong with (paglock) from #notbelong a, @rowstochangetype b where a.tablenick = b.tablenick and a.rowguid = b.rowguid select @tn = max(tablenick) from #notbelong where flag > -1 while @tn is not null begin select @tnstr = convert(nvarchar(11), @tn) exec @retcode = dbo.sp_MStablenamefromnick @tn, @table_name out, @pubid -- delete all rows indicated by the temp table IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 goto Failure end exec ('delete from ' + @table_name + ' where RowGuidCol in (select rowguid from #notbelong where tablenick = ' + @tnstr + ')' ) select @error=@@error, @rowcount=@@rowcount IF @error<>0 begin set @errcode= 0 goto Failure end select @rowsdeleted = @rowsdeleted + @rowcount -- move on to next nickname - decreasing makes delete order correct update #notbelong set flag = -1 where tablenick = @tn select @tn = max(tablenick) from #notbelong where flag > -1 end set @reason = formatmessage (20563) -- Moved out of partial range -- change tombstone type for the non-original rows (the rows that got deleted via expansion). update dbo.MSmerge_tombstone set type = 5, reason = @reason from #notbelong nb, dbo.MSmerge_tombstone ts where ts.tablenick = nb.tablenick and ts.rowguid = nb.rowguid and nb.original_row <> 1 -- open a cursor and get the new metadata types for the original rows and then call sp_MSsetrowmetadata for each original row. declare original_rows_2 CURSOR LOCAL FAST_FORWARD for select tablenick, rowguid, generation, lineage_new, metadatatype_new from #notbelong where original_row = 1 FOR READ ONLY open original_rows_2 fetch original_rows_2 into @tablenick, @rowguid, @generation, @lineage_new, @new_metatype while (@@fetch_status <> -1) begin -- Call sp_MSsetrowmetadata for only the original rows whose delete requests were passed in to this proc. exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype IF @@ERROR<>0 or @retcode<>0 begin set @errcode= 0 close original_rows_2 deallocate original_rows_2 goto Failure end fetch original_rows_2 into @tablenick, @rowguid, @generation, @lineage_new, @new_metatype end close original_rows_2 deallocate original_rows_2 commit tran end drop table #notbelong return 1 -- in sp_MSdelsubrows, 1=okay Failure: -- instead of checking @@trancount, check our bit flag. This is safer as we can rely on it whether or not we are called -- from an outer transaction. if (@transaction_started = 1) begin rollback tran start_of_batch commit tran end drop table #notbelong if @errcode = 1 set @errcode = 0 return(@errcode) -- in sp_MSdelsubrows, 0=error go exec dbo.sp_MS_marksystemobject sp_MSdelsubrowsbatch go grant exec on dbo.sp_MSdelsubrowsbatch to public go raiserror('Creating procedure sp_MSscriptviewproc', 0,1) go create procedure dbo.sp_MSscriptviewproc ( @viewname sysname, @ownername sysname, @procname nvarchar(290), @rgcol sysname, @objid int = NULL -- for possible backward comp. issue ) as begin declare @retcode smallint declare @colname nvarchar(258) declare @view_id int declare @iscomputed tinyint declare @xtype sysname select @retcode=0 declare @proctext table (line_no int primary key identity(1,1), line nvarchar(4000)) if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end set nocount on select @procname=QUOTENAME(@procname) insert into @proctext (line) values ( 'create procedure dbo.' + @procname + ' (@tablenick int, @max_rows int = NULL, @guidlast uniqueidentifier = ''00000000-0000-0000-0000-000000000000'') AS set nocount on set rowcount 0 if @max_rows is not null begin -- used to select data for initial pop. of subscriber for dynamic filtered publication set rowcount @max_rows declare @lin varbinary (255) declare @cv varbinary (2048) declare @replnick int declare @objid int declare @ccols int select @objid = objid from sysmergearticles where nickname = @tablenick select @ccols = max(colid) from syscolumns where id = @objid exec dbo.sp_MSgetreplnick @nickname = @replnick out if (@@error <> 0) or @replnick IS NULL begin RAISERROR (14055, 11, -1) RETURN(1) end set @lin = { fn UPDATELINEAGE(0x0, @replnick, 1) } set @cv = { fn INITCOLVS(@ccols, @replnick) } select @tablenick, v.' + @rgcol + ', coalesce (c.generation,1), coalesce (c.lineage, @lin), coalesce (c.colv1, @cv)') if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end if @objid is not NULL and exists (select * from syscolumns where id = @objid and (iscomputed=1 or type_name(xtype)='timestamp')) begin select @view_id = object_id(@viewname) declare collist cursor local fast_forward for select name from syscolumns where id = @view_id order by colid asc for read only open collist fetch collist into @colname while (@@fetch_status <> -1) begin --since a view does not preserve computed/timestamp property, we have to rely on the base table select @iscomputed=iscomputed, @xtype=xtype from syscolumns where id = @objid and name=@colname if @iscomputed=0 and type_name(@xtype) <> 'timestamp' begin select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid' --which can cause problems and is not necessary. insert @proctext (line) values(' , v.' + @colname) if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end end fetch next from collist into @colname end end else begin insert into @proctext (line) values ( ' , v.*') if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end end insert into @proctext (line) values (' from ' + QUOTENAME(@ownername) + '.' + QUOTENAME(@viewname) + ' v left outer join dbo.MSmerge_contents c on v.' + @rgcol + ' = c.rowguid and c.tablenick = @tablenick where v.' + @rgcol + ' > @guidlast order by v.' + @rgcol + ' return (1) end insert into #belong (tablenick, rowguid, flag, skipexpand, partchangegen, joinchangegen) select ct.tablenick, ct.rowguid, 0, 0, ct.partchangegen, ct.joinchangegen from #contents_subset ct, ' + QUOTENAME(@ownername) + '.' + QUOTENAME(@viewname) + ' v where ct.tablenick = @tablenick and ct.rowguid = v.' + @rgcol + ' if @@ERROR <> 0 begin RAISERROR(''Error selecting from view'' , 16, -1) return (1) end') select line from @proctext order by line_no asc Failure: return @retcode end go exec dbo.sp_MS_marksystemobject sp_MSscriptviewproc grant execute on sp_MSscriptviewproc to public raiserror('Creating procedure sp_MSmakeviewproc', 0,1) GO create procedure sp_MSmakeviewproc ( @viewname sysname, @ownername sysname, @procname nvarchar(290), @rgcol sysname, @objid int = NULL --for possible backward comp. issue ) as begin declare @retcode smallint declare @command nvarchar(4000) declare @db_name sysname set nocount on select @retcode = 0 select @db_name = db_name() if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end select @command = N'exec dbo.sp_MSscriptviewproc @viewname = ' + fn_replmakestringliteral(@viewname) collate database_default + ' ,@ownername = ' + fn_replmakestringliteral(@ownername) collate database_default + ' ,@procname = ' + fn_replmakestringliteral(@procname) collate database_default + ' ,@rgcol = ' + fn_replmakestringliteral(@rgcol) collate database_default + ' ,@objid = ' + isnull(convert(nvarchar(20), @objid), N'null') if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end exec @retcode = master.dbo.xp_execresultset @command, @db_name if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end select @procname=QUOTENAME(@procname) exec dbo.sp_MS_marksystemobject @procname if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end exec ('grant exec on ' + @procname + ' to public') if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end Failure: return @retcode end go exec dbo.sp_MS_marksystemobject sp_MSmakeviewproc go create procedure sp_MScreatebeforetable @objid int AS declare @command nvarchar(4000) declare @objidstr nvarchar(12) declare @dbname sysname declare @oldname sysname declare @newname sysname declare @nameguid uniqueidentifier declare @before_rowguidname sysname declare @retcode int declare @tablenick int set nocount on set @before_rowguidname = NULL -- If no publication including this table needs before images, just return if not exists (select * from sysmergepublications p, sysmergearticles a where a.objid = @objid and p.pubid = a.pubid and p.keep_before_values = 1) return (0) select @tablenick = max(nickname) from sysmergearticles where objid = @objid if @tablenick is null return (1) -- If a before image table already exists for this table, we need to drop it and create a new one select @oldname = max(o.name) from sysobjects o, sysmergearticles a where a.objid = @objid and o.id = a.before_image_objid if @oldname is not null begin exec @retcode = sp_MShelpalterbeforetable @objid, @oldname if @@error<>0 or @retcode<>0 return (1) return(0) end -- If this table is not involved with any filters or join filters, don't bother if not exists (select * from sysmergesubsetfilters where art_nickname = @tablenick or join_nickname = @tablenick) and not exists (select * from sysmergearticles where nickname = @tablenick and datalength (subset_filterclause) > 1) return(0) -- Generate a unique name for our new table set @nameguid = newid() exec @retcode = dbo.sp_MSguidtostr @nameguid, @newname out if @@ERROR <>0 OR @retcode <>0 return (1) set @newname = 'MS_bi' + @newname -- Call xp_execresultset with helper function command set @objidstr = convert(nvarchar(12), @objid) set @dbname = db_name() set @command = 'exec dbo.sp_MShelpcreatebeforetable ' + @objidstr + ', ''' + @newname + '''' exec @retcode = master..xp_execresultset @command, @dbname if @@error<>0 or @retcode<>0 return (1) select @before_rowguidname=name from syscolumns where id=@objid and columnproperty(@objid, name , 'isrowguidcol')=1 if @before_rowguidname is not NULL begin exec ('grant select (' + @before_rowguidname + ') on ' + @newname + ' to public') if @@ERROR<>0 return (1) end /* grant select to system_delete column */ exec ('grant select (system_delete, generation), update(generation), delete on ' + @newname + ' to public') if @@ERROR<>0 return (1) exec dbo.sp_MS_marksystemobject @newname if @@ERROR<>0 return (1) update sysmergearticles set before_image_objid = object_id( @newname ) where objid = @objid return(0) go exec dbo.sp_MS_marksystemobject sp_MScreatebeforetable go create procedure sp_MShelpcreatebeforetable @objid int, @newname sysname AS declare @command nvarchar(1000) declare @retcode int declare @include int declare @tablenick int declare @colpat nvarchar(130) declare @colname nvarchar(140) declare @typename nvarchar(140) declare @colid smallint declare @colidstr nvarchar(3) declare @status tinyint declare @len smallint declare @prec smallint declare @scale int declare @isnullable tinyint declare @cMaxIndexLength int set @cMaxIndexLength= 900 -- max index column size in SQL 2000 set nocount on select @tablenick = max(nickname) from sysmergearticles where objid = @objid if @tablenick is null begin select 'a' = 'raiserror (''Error creating before image table'' , 16, -1)' return (1) end -- create temp table to select the command text out of create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) set @command = 'create table ' + @newname + '(' insert into #tempcmd (phase, cmdtext) values (1, @command) -- Loop over the columns and see which ones we include declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, status, type_name(xtype), length, prec, scale, isnullable, colid from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid FOR READ ONLY open col_cursor fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid while (@@fetch_status <> -1) begin set @include = 0 set @colpat = '%' + @colname + '%' if columnproperty( @objid, @colname , 'isrowguidcol')=1 begin set @include = 1 end else begin -- does updating this column change membership in a partial replica? if exists (select * from sysmergearticles where objid = @objid and subset_filterclause like @colpat) set @include = 1 else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick and join_filterclause like @colpat) set @include = 1 else if exists (select * from sysmergesubsetfilters where join_nickname = @tablenick and join_filterclause like @colpat) set @include = 1 end -- If we want this column, map its type and insert a row to temp table if @include = 1 begin if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes set @len = @len/2 exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@ERROR<>0 or @retcode<>0 goto Failure if @typename not in ('text', 'ntext','image') begin select @colname = N'[' + replace(@colname, N']', N']]') + N']' if @isnullable = 1 set @command = @colname + ' ' + @typename + ' NULL, ' else set @command = @colname + ' ' + @typename + ' NOT NULL, ' -- Insert the part of create table command for this column insert into #tempcmd (phase, cmdtext) values (1, @command) -- Insert a create index command if column is not too long if (@len <= @cMaxIndexLength) begin set @colidstr =convert(nvarchar(3), @colid) set @command = 'create index ' + @newname + '_' + @colidstr + ' on ' + @newname + ' (' + @colname + ') ' insert into #tempcmd (phase, cmdtext) values (2, @command) end end end /* Repeat the loop with next column */ fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid end close col_cursor deallocate col_cursor -- Insert last column, generation set @command = 'generation int NOT NULL, system_delete bit default(0)) ' insert into #tempcmd (phase, cmdtext) values (1, @command) set @command = 'create clustered index ' + @newname + '_gen on ' + @newname + '(generation) ' insert into #tempcmd (phase, cmdtext) values (2, @command) select cmdtext from #tempcmd order by phase, step drop table #tempcmd return(0) Failure: close col_cursor deallocate col_cursor return(1) go exec dbo.sp_MS_marksystemobject sp_MShelpcreatebeforetable go create procedure sp_MShelpalterbeforetable @objid int, @biname sysname AS declare @command nvarchar(4000) declare @retcode int declare @include int declare @tablenick int declare @colpat nvarchar(130) declare @colname nvarchar(130) declare @typename sysname declare @colid smallint declare @colidstr nvarchar(3) declare @status tinyint declare @len smallint declare @prec smallint declare @scale int declare @isnullable tinyint declare @bi_objid int set nocount on declare @cMaxIndexLength int set @cMaxIndexLength= 900 -- max index column size in SQL 2000 select @tablenick = max(nickname) from sysmergearticles where objid = @objid if @tablenick is null return (1) select @bi_objid = OBJECT_ID(@biname) -- Loop over the columns and see which ones we include declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, status, type_name(xtype), length, prec, scale, isnullable, colid from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid FOR READ ONLY open col_cursor fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid while (@@fetch_status <> -1) begin set @include = 0 set @colpat = '%' + @colname + '%' if not exists (select * from syscolumns where id = @bi_objid and QUOTENAME(name) = QUOTENAME('system_delete')) begin set @command = 'alter table ' + @biname + ' ADD system_delete bit default(0) ' execute ( @command ) if @@ERROR<>0 goto errlabel /* grant select to system_delete column */ exec ('grant select (system_delete) on ' + @biname + ' to public') if @@ERROR<>0 goto errlabel end exec ('grant select (generation), update(generation), delete on ' + @biname + ' to public') if @@ERROR<>0 goto errlabel -- Is this column already in the before image table? -- or the column is not in the vertical partitioning? if exists (select * from syscolumns where id = @bi_objid and name = @colname) OR exists (select * from sysmergearticles where objid=@objid and not exists ( select * from syscolumns where id = sync_objid and name = @colname)) begin goto fetchnext end -- does updating this column change membership in a partial replica? if exists (select * from sysmergearticles where objid = @objid and subset_filterclause like @colpat) set @include = 1 else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick and join_filterclause like @colpat) set @include = 1 else if exists (select * from sysmergesubsetfilters where join_nickname = @tablenick and join_filterclause like @colpat) set @include = 1 -- If we want this column, map its type and insert a row to temp table if @include <> 1 begin goto fetchnext end if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes set @len = @len/2 exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale if @@ERROR<>0 or @retcode<>0 goto errlabel if @typename not in ('text', 'ntext','image') begin set @colname = QUOTENAME(@colname) -- Always make columns nullable when we add them because we might have -- existing rows in the before image table. set @command = 'alter table ' + @biname + ' ADD ' + @colname + ' ' + @typename + ' NULL ' execute ( @command ) if @@ERROR<>0 goto errlabel -- Insert a create index command if column is not too long if (@len <= @cMaxIndexLength) begin set @colidstr =convert(nvarchar(3), @colid) set @command = 'create index ' + @biname + '_' + @colidstr + ' on ' + @biname + ' (' + @colname + ')' execute ( @command ) if @@ERROR<>0 goto errlabel end end fetchnext: /* Repeat the loop with next column */ fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid end close col_cursor deallocate col_cursor return 0 errlabel: close col_cursor deallocate col_cursor return 1 go exec dbo.sp_MS_marksystemobject sp_MShelpalterbeforetable go create procedure sp_MSgetbeforetableinsert @objid int, @inscommand nvarchar(2000) output AS declare @before_objid int declare @before_name sysname declare @collist nvarchar(1000) declare @colname sysname -- Do we have a before table? select @before_objid = max(before_image_objid) from sysmergearticles where objid = @objid and before_image_objid is not null select @before_name = OBJECT_NAME(@before_objid) if @before_name is null begin set @inscommand = '' return 0 end set @collist = '' -- Loop over columns to make the column list for the insert / select command declare col_cursor CURSOR LOCAL FAST_FORWARD for select name from syscolumns where id = @before_objid and name <> 'generation' and name <> 'system_delete' order by colid FOR READ ONLY open col_cursor fetch next from col_cursor into @colname while (@@fetch_status <> -1) begin set @collist = @collist + QUOTENAME(@colname) + ', ' fetch next from col_cursor into @colname end close col_cursor deallocate col_cursor -- Our list has all of the columns except generation since that gets set to a local variable -- Make the insert command set @inscommand = 'insert into ' + QUOTENAME(@before_name) + ' ( ' + @collist + ' generation) select ' + @collist + ' a.gen_cur from deleted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as a ' return 0 go exec dbo.sp_MS_marksystemobject sp_MSgetbeforetableinsert go create procedure sp_MSfixupbeforeimagetables(@pubid uniqueidentifier) AS begin -- This procedure updates the generations in the existing rows in the before-image -- tables of all articles in the specified publication to the current gen_cur value for -- the respective article. -- This is done after the subscription has been reinitialized (at a republisher). The reason -- is that the genhistory rows at the subscriber no longer correspond to the values before -- the reinit. They are simply bcp-ed in from the bcp generated at the publisher. -- Setting the existing before image rows to gen_cur has the effect of avoiding the retention- -- based cleanup of these rows based on invalid coldate values (since they are in context of -- the publisher). The cleanup thus gets delayed to when the current gen_cur value expires retention. declare @before_image_tablename sysname, @gencur int, @gencur_str nvarchar(20), @cmd nvarchar(256) if @pubid is null return 0 declare beforeimagetables_cursor CURSOR LOCAL FAST_FORWARD for select distinct object_name(before_image_objid), gen_cur from sysmergearticles where artid in (select artid from sysmergearticles where pubid = @pubid) and before_image_objid is not null FOR READ ONLY open beforeimagetables_cursor fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur while (@@fetch_status <> -1) begin if @before_image_tablename is not null begin select @gencur_str = convert(nvarchar(20), @gencur) select @cmd = 'update ' + @before_image_tablename + ' set generation = ' + @gencur_str exec (@cmd) end fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur end close beforeimagetables_cursor deallocate beforeimagetables_cursor return 0 end go exec dbo.sp_MS_marksystemobject sp_MSfixupbeforeimagetables go grant exec on dbo.sp_MSfixupbeforeimagetables to public go create procedure sp_MSreplcheck_permission( @objid int, @type int, @permissions int) AS declare @tablename sysname declare @ownername sysname select @ownername=user_name(uid) from sysobjects where id=@objid select @tablename=object_name(@objid) -- bypass this checking for dbo or member of db_owner. if is_srvrolemember('sysadmin') = 1 or is_member ('db_owner') = 1 return (0) if @permissions=0 return (0) --insert if @type=1 and @permissions & 1 = 1 and permissions(@objid) & 0x8=0 return (1) --update if @type=2 and @permissions & 2 = 2 and permissions(@objid) & 0x2=0 return (1) --delete if @type=3 and @permissions & 4 = 4 and permissions(@objid) & 0x10=0 return (1) return (0) GO grant exec on sp_MSreplcheck_permission to public go exec dbo.sp_MS_marksystemobject sp_MSreplcheck_permission go create procedure sp_MSinserterrorlineage (@tablenick int, @rowguid uniqueidentifier, @lineage varbinary(255)) as if exists (select * from MSmerge_errorlineage where tablenick = @tablenick and rowguid = @rowguid) update MSmerge_errorlineage set lineage = @lineage where tablenick = @tablenick and rowguid = @rowguid else insert into MSmerge_errorlineage (tablenick, rowguid, lineage) values (@tablenick, @rowguid, @lineage) if @@ERROR <> 0 return (1) return 0 go grant exec on sp_MSinserterrorlineage to public go exec dbo.sp_MS_marksystemobject sp_MSinserterrorlineage go create procedure sp_MSevalsubscriberinfo (@pubid uniqueidentifier) as declare @expr nvarchar(500) select @expr = validate_subscriber_info from sysmergepublications where pubid = @pubid exec ('select ' + @expr) if @@error<>0 return(1) return(0) go grant exec on sp_MSevalsubscriberinfo to public go exec dbo.sp_MS_marksystemobject sp_MSevalsubscriberinfo go create procedure sp_MSsetsubscriberinfo (@pubid uniqueidentifier, @expr nvarchar(500)) as update sysmergepublications set validate_subscriber_info = @expr where pubid = @pubid go grant exec on sp_MSsetsubscriberinfo to public go exec dbo.sp_MS_marksystemobject sp_MSsetsubscriberinfo go create procedure sp_MSgetsubscriberinfo (@pubid uniqueidentifier) as declare @expr nvarchar(500) select @expr = validate_subscriber_info from sysmergepublications where pubid = @pubid -- Return the value -- select @expr go grant exec on sp_MSgetsubscriberinfo to public go exec dbo.sp_MS_marksystemobject sp_MSgetsubscriberinfo go raiserror('Creating procedure sp_MSmakectsview', 0,1) GO create procedure sp_MSmakectsview @publication sysname, @ctsview sysname, @dynamic_snapshot_views_table_name sysname = null AS set nocount on declare @pubid uniqueidentifier declare @artid uniqueidentifier declare @pubidstr nvarchar(40) declare @artidstr nvarchar(40) declare @objid int declare @tablenick int declare @new_inactive int declare @new_active int declare @tablenickstr nvarchar(12) declare @command_piece nvarchar(2000) declare @rowguidcolname nvarchar(140) declare @view_type int declare @view_name nvarchar(270) declare @or_after_first nvarchar(100) declare @select_command nvarchar(4000) declare @retcode int declare @generate_per_article bit declare @newidstr nvarchar(40) declare @newid uniqueidentifier /* By default the @generate_per_article is OFF */ set @generate_per_article = 0 set @new_inactive = 5 /* value of SQLDMOArtStat_New_Inactive */ set @new_active = 6 /* value of SQLDMOArtStat_New_Active */ if @ctsview IS NULL set @generate_per_article = 1 select @retcode = 0 set @or_after_first = '' select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() select @newid = newid() exec @retcode = dbo.sp_MSguidtostr @newid, @newidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) /* Check for the no filtering cases */ if not exists (select * from sysmergesubsetfilters where pubid = @pubid) and not exists (select * from sysmergearticles where pubid = @pubid and len(subset_filterclause) > 0) begin if @generate_per_article = 1 begin set @ctsview = 'ctsv_' + @newidstr select @command_piece = N'if object_id(''' + @ctsview + ''') is NOT NULL drop view ' + @ctsview exec ( @command_piece ) if @@ERROR <>0 return (1) exec ( @command_piece ) end set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents ' /* Are there any published tables not in this publication - any articles that needs to be excluded */ if exists (select * from sysmergearticles where ((pubid=@pubid and (status=@new_inactive or status=@new_active)) or (pubid <> @pubid and nickname not in (select nickname from sysmergearticles where pubid = @pubid)))) begin /* pubidstr is needed in GUID format */ set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' set @command_piece = @command_piece + ' where tablenick in (select nickname from sysmergearticles where status<>5 and status<>6 and pubid = ' + @pubidstr + ')' --5 and 6 are the new article statuses end if @generate_per_article = 1 begin exec ( @command_piece ) if @@ERROR <>0 return (1) select @ctsview end else begin /* Now select our simple view syntax and we are done with this simple unfiltered case */ select @command_piece end return 0 end /* create temp table to insert into and select commands out of */ create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null) if @generate_per_article = 0 begin set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents where ' insert into #tempcmd (phase, cmdtext) values (1, @command_piece) end /* Initialize for loop over articles in this publication */ select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive while @tablenick is not null begin if @dynamic_snapshot_views_table_name is null or @dynamic_snapshot_views_table_name = N'' begin select @objid = objid, @view_type = view_type, @view_name = object_name(sync_objid) from sysmergearticles where pubid = @pubid and nickname = @tablenick end else begin select @select_command = ' select @objid = sma.objid, @view_type = sma.view_type, @view_name = dsvt.dynamic_snapshot_view_name from sysmergearticles sma inner join ' + @dynamic_snapshot_views_table_name + ' dsvt on dsvt.artid = sma.artid where pubid = @pubid and nickname = @tablenick' exec sp_executesql @select_command, N'@objid int output, @view_type int output, @view_name nvarchar(270) output, @pubid uniqueidentifier, @tablenick int', @objid = @objid output, @view_type = @view_type output, @view_name = @view_name output, @pubid = @pubid, @tablenick = @tablenick if @@error<>0 return(1) end select @rowguidcolname = name from syscolumns where id = @objid and columnproperty(id, name, 'isrowguidcol')=1 set @rowguidcolname = QUOTENAME(@rowguidcolname) set @view_name = QUOTENAME(@view_name) set @tablenickstr = convert(nchar(12), @tablenick) if @view_type <> 0 begin if @generate_per_article = 0 begin set @command_piece = @or_after_first + ' (tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @rowguidcolname + ' from ' + @view_name + '))' end else begin select @artid = artid from sysmergearticles where nickname = @tablenick and pubid = @pubid set @artidstr = '''' + convert(nchar(36), @artid) + '''' exec @retcode = dbo.sp_MSguidtostr @artid, @artidstr out if @@ERROR <>0 OR @retcode <> 0 return (1) set @ctsview = 'ctsv_' + @newidstr + @artidstr select @command_piece = N'if object_id(''' + @ctsview + ''') is NOT NULL drop view ' + @ctsview exec ( @command_piece ) if @@ERROR <>0 return (1) set @command_piece = 'create view dbo.' + @ctsview + ' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @rowguidcolname + ' from ' + @view_name + ')) ' exec ( @command_piece ) if @@ERROR <>0 return (1) end end else set @command_piece = @or_after_first + ' (tablenick = ' + @tablenickstr + ')' if @generate_per_article = 0 begin insert into #tempcmd (phase, cmdtext) values (2, @command_piece) end else begin insert into #tempcmd (phase, cmdtext) values (2, @ctsview) end /* Advance to next article and repeat the loop */ select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive /* make it so that any subsequent selects in the view are preceded by the word UNION */ /* using OR to replace 'UNION ALL', which is equivalent to workaround a bug */ set @or_after_first = ' OR ' end /* final steps: select out the text and drop the temp table */ if @generate_per_article = 0 begin select cmdtext from #tempcmd order by phase, step end else /* Select the view names so that the caller can query them so they can be BCP out and dropped later */ begin select cmdtext from #tempcmd where phase = 2 order by step end drop table #tempcmd return(0) go exec dbo.sp_MS_marksystemobject sp_MSmakectsview go grant exec on dbo.sp_MSmakectsview to public go create procedure sp_MSinsertgenerationschemachanges @publication sysname AS declare @mingen int declare @lastrecgen int declare @lastrecguid uniqueidentifier declare @lastsentgen int declare @lastsentguid uniqueidentifier declare @db_name sysname declare @repid uniqueidentifier declare @pubid uniqueidentifier declare @schemaversion int declare @schemaguid uniqueidentifier declare @schematype int declare @schematext nvarchar(2000) declare @artid uniqueidentifier declare @retcode int set nocount on set @lastrecgen = NULL set @lastsentgen = NULL begin tran save TRAN MSinsertgenerationschemachanges set @db_name = db_name() select @pubid = pubid from sysmergepublications where name = @publication and publisher = @@SERVERNAME and publisher_db = @db_name if @pubid IS NULL BEGIN RAISERROR (20026, 16, -1, @publication) RETURN (1) END SELECT @repid = subid FROM sysmergesubscriptions where pubid = @pubid and subid = @pubid if @repid is NULL begin RAISERROR(20021, 16, -1) return (1) end select @mingen = min(generation) from dbo.MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000' if @mingen IS NOT NULL select @lastrecgen = max(generation) from dbo.MSmerge_genhistory where generation < @mingen if @lastrecgen IS NOT NULL begin select @lastrecguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastrecgen set @artid = NULL set @schematype = 5 /* last rec gen schema type */ select @schematext = 'exec dbo.sp_MSsetlastrecgen ' + '''' + convert(nchar(36), @repid) + '''' + ',' + '''' + convert(nvarchar(10), @lastrecgen) + '''' + ',' + '''' + convert(nchar(36), @lastrecguid) + '''' if exists (select * from sysmergeschemachange where pubid = @pubid AND schematype = @schematype) begin /* Select the existing schema guid */ select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange where pubid = @pubid AND schematype = @schematype /* ** Update the schema change version */ exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 goto FAILURE end else begin /* Insert the schema change */ select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange /* generate a new schema guid */ set @schemaguid = newid() exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 goto FAILURE end end select @lastsentgen = max(generation) from dbo.MSmerge_genhistory where guidlocal <> '00000000-0000-0000-0000-000000000000' and (art_nick = 0 or art_nick is NULL or art_nick in (select nickname from sysmergearticles where pubid = @pubid)) if @lastsentgen IS NOT NULL begin select @lastsentguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastsentgen set @artid = NULL set @schematype = 6 /* last sent gen schema type */ select @schematext = 'exec dbo.sp_MSsetlastsentgen ' + '''' + convert(nchar(36), @repid) + '''' + ',' + '''' + convert(nvarchar(10), @lastsentgen) + '''' + ',' + '''' + convert(nchar(36), @lastsentguid) + '''' if exists (select * from sysmergeschemachange where pubid = @pubid AND schematype = @schematype) begin /* Select the existing schema guid */ select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange where pubid = @pubid AND schematype = @schematype /* ** Update the schema change version */ exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 goto FAILURE end else begin /* Insert the schema change */ select @schemaversion = schemaversion from sysmergeschemachange if (@schemaversion is NULL) set @schemaversion = 1 else select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange /* generate a new schema guid */ set @schemaguid = newid() exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext if @@error <> 0 or @retcode <> 0 goto FAILURE end end COMMIT TRAN RETURN 0 FAILURE: /* UNDONE : This code is specific to 6.X nested transaction semantics */ if @@TRANCOUNT > 0 begin ROLLBACK TRANSACTION MSinsertgenerationschemachanges COMMIT TRANSACTION end RETURN 1 go exec dbo.sp_MS_marksystemobject sp_MSinsertgenerationschemachanges go grant exec on dbo.sp_MSinsertgenerationschemachanges to public go CREATE PROCEDURE sp_MSalreadyhavegeneration (@genguid uniqueidentifier, @subscribernick int) as declare @nicknames varbinary(1000) declare @offset int /* ** Check input parameter */ if (@genguid is null) begin RAISERROR(14043, 16, -1, '@genguid') return (1) end select @nicknames = nicknames from dbo.MSmerge_genhistory where guidsrc = @genguid if @nicknames is null begin -- what is the appropriate error to return? RAISERROR(21333, 16, -1) return (1) end set @offset = 1 while @offset < len(@nicknames) begin -- If the subscriber nickname is already in the list just return if (convert(int, substring(@nicknames, @offset, 4)) = @subscribernick) return (0) set @offset = @offset + 4 end -- Subscriber nickname is not in the array. Add it so that we won't send this gen -- back down in the return message. (Would waste bandwidth...) set @nicknames = convert(binary(4), @subscribernick) + @nicknames update dbo.MSmerge_genhistory set nicknames = @nicknames where guidsrc = @genguid return (0) go exec dbo.sp_MS_marksystemobject sp_MSalreadyhavegeneration go grant exec on dbo.sp_MSalreadyhavegeneration to public GO CREATE PROCEDURE sp_MSgettablecontents @pubid uniqueidentifier as declare @tablenick int declare @new_active int declare @new_inactive int declare @artname sysname declare @view_name nvarchar(60) declare @rowguidcolname sysname declare @sync_viewname sysname declare @quoted_sync_viewname nvarchar(144) declare @quoted_view_name nvarchar(144) declare @nickname int declare @objid int declare @tablenickstr nvarchar(12) select @new_inactive = 5 --special article status for adding article after snapshot select @new_active = 6 --special article status for adding article after snapshot select Top 1 @tablenick = nickname, @sync_viewname=object_name(sync_objid), @objid=objid, @artname=name from sysmergearticles where pubid = @pubid and (status=@new_inactive or status=@new_active) order by nickname ASC while @objid is not null begin set @tablenickstr = convert(nchar(12), @tablenick) select @rowguidcolname = name from syscolumns where id = @objid and columnproperty(id, name, 'isrowguidcol')=1 select @view_name='MSmerge_contents_' + @artname select @quoted_view_name=QUOTENAME(@view_name) --Must drop the view (if exists) and recreate if exists (select * from sysobjects where name=@view_name and xtype = 'V') begin exec ('drop view ' + @quoted_view_name) end select @quoted_sync_viewname=QUOTENAME(@sync_viewname) exec('create view ' + @quoted_view_name + ' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in (select ' + @rowguidcolname + ' from ' + @quoted_sync_viewname + '))') select @objid = NULL select Top 1 @tablenick = nickname,@sync_viewname=object_name(sync_objid), @objid=objid, @artname=name from sysmergearticles where pubid = @pubid and nickname>@tablenick and (status=@new_inactive or status=@new_active) order by nickname ASC insert #temp_table_for_systable_view(contentsview) values(@view_name) end go exec dbo.sp_MS_marksystemobject sp_MSgettablecontents go grant exec on dbo.sp_MSgettablecontents to public GO CREATE PROCEDURE sp_MSdelgenzero as delete from dbo.MSmerge_contents where generation = 0 or generation = 1 go exec dbo.sp_MS_marksystemobject sp_MSdelgenzero go grant exec on dbo.sp_MSdelgenzero to public GO -- -- Name: sp_MSmakedynsnapshotvws -- -- Description: This procedure generates temporary sync-views on top of -- table articles specific to a particular dynamic filter login by -- replacing all occurrances of the suser_sname() intrinsic -- function in the regular article sync-views with the specified -- dynamic filter login value. -- -- Parameters: @publication sysname -- @dynamic_filter_login sysname -- -- Notes: This procedure should be used by dynamic snapshot processing -- only. A valid regular snapshot is assumed to have been generated -- by the time the snapshot agent calls this procedure. If the specified -- @dynamic_filter_login is empty or null, the current value -- of suser_sname() will be used. -- -- Result: sysname - article_name -- sysname - dynamic_snapshot_view_name -- The result set basically describes the mapping between articles and -- the dynamic snapshot views to be used for the dynamic snapshot -- generation session that initiated the call. The exceptions being the -- 'dynamic snapshot view name' column of the first row is actually the -- name of the global temporary table for tracking the dynamic snapshot -- while the 'dynamic snapshot view name' column of the 2nd row is -- actually the owner name of all the generated dynamic snapshot -- views. (Note: all generated views belong to the same owner) -- -- Returns: 0 - succeeded -- 1 - failed -- -- Security: Execute permission of this procedure is granted to public. A -- procedural security check is made inside the stored procedure -- to make sure that the caller is either a member of the -- 'sysadmin' server role or the 'db_owner' database role. -- raiserror('Creating procedure sp_MSmakedynsnapshotvws', 0,1) go create procedure sp_MSmakedynsnapshotvws( @publication sysname, @dynamic_filter_login sysname ) as begin set nocount on declare @retcode int declare @dynamic_snapshot_views_table_name sysname declare @string_literalized_dynamic_filter_login nvarchar(4000) declare @articles_cursor_allocated bit, @articles_cursor_opened bit declare @pubid uniqueidentifier select @articles_cursor_allocated = 0 select @articles_cursor_opened = 0 select @retcode = 0 -- Security check exec @retcode = sp_MSreplcheck_publish if @retcode <> 0 or @@error <> 0 return 1 if @dynamic_filter_login is null or @dynamic_filter_login = N'' begin select @dynamic_filter_login = suser_sname() end if @@error<>0 begin return 1 end select @string_literalized_dynamic_filter_login = fn_replmakestringliteral(@dynamic_filter_login) collate database_default -- Compute the proper view creation order, code is copied from sp_MSpublicationview declare @progress int declare @article_level int select @progress = 1 select @article_level = 0 select @pubid = pubid from sysmergepublications where upper(publisher) = upper(@@servername) and publisher_db = db_name() and name = @publication if @pubid is null begin raiserror(20026,11,-1,@publication) end create table #creation_order ( creation_order int identity not null, art_nick int not null, article_level int not null ) if @@error<>0 begin return 1 end while @progress > 0 begin insert into #creation_order(art_nick, article_level) select nickname, @article_level from sysmergearticles where pubid=@pubid and nickname not in (select art_nick from #creation_order) and nickname not in (select art_nickname from sysmergesubsetfilters where pubid=@pubid and join_nickname not in (select art_nick from #creation_order)) select @progress = @@rowcount select @article_level = @article_level + 1 end -- Create temp table for storing the view definitions and -- also for acting as a symbol table for views create table #view_defs_and_syms ( creation_order int identity, original_view_name sysname collate database_default, dynamic_snapshot_view_name sysname collate database_default, dynamic_snapshot_view_definition nvarchar(3500) collate database_default ) if @@error<>0 begin return 1 end -- By the time this procedure is called by the snapshot agent, a regular -- snapshot is assumed to have been generated. -- The specified publication is assumed to have dynamic filtering enabled -- Open a transaction to make sure that no stale temporary views -- can be lying around for longer than they have to. begin transaction save transaction sp_MSmakedynsnapshotartvws -- Create a global temporary table to track the sync-views -- that we are about to create here. select @dynamic_snapshot_views_table_name = N'##DYN_VIEWS_' + replace(convert(nvarchar(36), newid()), N'-', N'_') exec('create table ' + @dynamic_snapshot_views_table_name + ' ( artid uniqueidentifier primary key, dynamic_snapshot_view_name sysname not null unique )') if @@error<>0 begin goto Failure end declare hArticles cursor local fast_forward for select artid, sync_objid from sysmergearticles sma inner join #creation_order co on sma.nickname = co.art_nick where sma.pubid = @pubid order by co.creation_order asc if @@error<>0 begin goto Failure end select @articles_cursor_allocated = 1 open hArticles if @@error<>0 begin goto Failure end select @articles_cursor_opened = 1 declare @sync_objid int declare @artid uniqueidentifier declare @dynamic_snapshot_view_name sysname declare @original_view_name sysname declare @insert_command nvarchar(4000) declare @script_view_command nvarchar(4000) declare @min_id int declare @db_name sysname declare @original_view_definition nvarchar(4000) select @db_name = db_name() -- Stage 1: Build up the symbol table with -- regular sync view definitions fetch hArticles into @artid, @sync_objid while (@@fetch_status<>-1) begin -- Get name of the original sync view select @original_view_name = object_name(@sync_objid) -- Construct name for the dynamic snapshot view select @dynamic_snapshot_view_name = N'DYN_VIEW_' + replace(convert(nvarchar(36), newid()), N'-', N'_') -- Insert a row into the symbol and view def table -- for further processing select @original_view_definition = text from dbo.syscomments where id = @sync_objid insert #view_defs_and_syms values (quotename(@original_view_name), @dynamic_snapshot_view_name, @original_view_definition) if @@error<>0 begin goto Failure end select @insert_command = N'insert ' + @dynamic_snapshot_views_table_name + ' values (@artid, @dynamic_snapshot_view_name)' -- Insert the mapping into the global temp table exec @retcode = sp_executesql @insert_command, N'@artid uniqueidentifier, @dynamic_snapshot_view_name sysname', @artid = @artid, @dynamic_snapshot_view_name = @dynamic_snapshot_view_name if @@error<>0 or @retcode<>0 begin goto Failure end -- Insert the view name into the global tracking table so it is -- guaranteed to be cleaned up properly insert MSdynamicsnapshotviews values (@dynamic_snapshot_view_name) if @@error<>0 begin goto Failure end fetch hArticles into @artid, @sync_objid end close hArticles select @articles_cursor_opened = 0 deallocate hArticles select @articles_cursor_allocated = 0 -- Stage 2: Fix-up all the internal references to non-dynamic snapshot -- views inside the dynamic snapshot view definitions and -- create the views along the way. -- Cursor state variables for ensuring proper cursor cleanup declare @viewdefs_cursor_allocated bit, @viewdefs_cursor_opened bit, @symbols_cursor_allocated bit, @symbols_cursor_opened bit declare @dynamic_snapshot_view_definition nvarchar(4000) declare @dynamic_snapshot_view_name_symbol sysname declare @qualified_dynamic_snapshot_view_name nvarchar(4000) select @viewdefs_cursor_allocated = 0, @viewdefs_cursor_opened = 0, @symbols_cursor_allocated = 0, @symbols_cursor_opened = 0 -- Declare a cursor for each dynamic snapshot view definition declare hViewDefs cursor local fast_forward for select dynamic_snapshot_view_name, dynamic_snapshot_view_definition from #view_defs_and_syms order by creation_order asc if @@error<>0 begin goto Failure end select @viewdefs_cursor_allocated = 1 -- Declare a static cursor for symbol lookup so we can go back to the -- first row without tearing down the cursor declare hSymbols cursor local static for select original_view_name, dynamic_snapshot_view_name from #view_defs_and_syms if @@error<>0 begin goto Failure end select @symbols_cursor_allocated = 1 open hViewDefs if @@error<>0 begin goto Failure end select @viewdefs_cursor_opened = 1 open hSymbols if @@error<>0 begin goto Failure end select @symbols_cursor_opened = 1 -- For each view definition... fetch hViewDefs into @dynamic_snapshot_view_name, @dynamic_snapshot_view_definition while (@@fetch_status<>-1) begin -- For each dynamic snapshot view name to original view name mapping... fetch first from hSymbols into @original_view_name, @dynamic_snapshot_view_name_symbol while (@@fetch_status<>-1) begin -- Do view name replacement select @qualified_dynamic_snapshot_view_name = N'[dbo].' + quotename(@dynamic_snapshot_view_name_symbol) select @dynamic_snapshot_view_definition = replace(@dynamic_snapshot_view_definition, @original_view_name, @qualified_dynamic_snapshot_view_name) fetch hSymbols into @original_view_name, @dynamic_snapshot_view_name_symbol end -- Replace suser_sname() with string literalized version of the given -- dynamic filter login select @dynamic_snapshot_view_definition = replace(@dynamic_snapshot_view_definition collate SQL_Latin1_General_Cp1_CI_AS, N'suser_sname()' collate SQL_Latin1_General_Cp1_CI_AS, @string_literalized_dynamic_filter_login collate SQL_Latin1_General_Cp1_CI_AS) -- Replace system_user with string literalized version of the given -- dynamic filter login select @dynamic_snapshot_view_definition = replace(@dynamic_snapshot_view_definition collate SQL_Latin1_General_Cp1_CI_AS, N'system_user' collate SQL_Latin1_General_Cp1_CI_AS, @string_literalized_dynamic_filter_login collate SQL_Latin1_General_Cp1_CI_AS) if datalength(@dynamic_snapshot_view_definition) = 7000 begin raiserror(21387,16,-1) goto Failure end -- Create the view and mark it as a system object exec (@dynamic_snapshot_view_definition) if @@error<>0 begin goto Failure end exec @retcode = sp_MS_marksystemobject @dynamic_snapshot_view_name if @@error<>0 or @retcode<>0 begin goto Failure end fetch hViewDefs into @dynamic_snapshot_view_name, @dynamic_snapshot_view_definition end close hViewDefs deallocate hViewDefs close hSymbols deallocate hSymbols drop table #view_defs_and_syms drop table #creation_order commit transaction -- Construct the result set declare @dynamic_snapshot_view_owner sysname -- Since all views will have the same owner, we may as well just -- find it once and then return it in the second row of the result set -- (see header) -- Note: Even the cursors are closed, the @dynamic_snapshot_view_name -- variable should still contain a valid value select @dynamic_snapshot_view_owner = user_name(uid) from sysobjects where name = @dynamic_snapshot_view_name declare @result_command nvarchar(4000) select @result_command = ' select ''article_name'' = null, ''dynamic_snapshot_view_name'' = ' + fn_replmakestringliteral(@dynamic_snapshot_views_table_name) collate database_default + N' union all select null, ' + coalesce(fn_replmakestringliteral(@dynamic_snapshot_view_owner) collate database_default, N'null') collate database_default + N' union all select sma.name, dsvt.dynamic_snapshot_view_name from sysmergearticles sma inner join ' + @dynamic_snapshot_views_table_name + N' dsvt on sma.artid = dsvt.artid where sma.pubid = ''' + convert(nvarchar(128), @pubid) + '''' exec (@result_command) if @@error<>0 return 1 return 0 Failure: if @articles_cursor_opened = 1 begin close hArticles end if @articles_cursor_allocated = 1 begin deallocate hArticles end if @viewdefs_cursor_opened = 1 begin close hViewDefs end if @viewdefs_cursor_allocated = 1 begin deallocate hViewDefs end if @symbols_cursor_opened = 1 begin close hSymbols end if @symbols_cursor_allocated = 1 begin deallocate hSymbols end drop table #view_defs_and_syms drop table #creation_order rollback transaction sp_MSmakedynsnapshotartvws commit transaction return 1 end go exec sp_MS_marksystemobject 'sp_MSmakedynsnapshotvws' grant execute on sp_MSmakedynsnapshotvws to public go -- -- Name: sp_MSdropdynsnapshotvws -- -- Description: This procedure is used for dropping the dynamic snapshot views -- for a particular dynamic snapshot generation session. -- -- Parameter: dynamic_snaphsot_views_table sysname -- -- Returns: 0 - succeeded -- 1 - failed -- -- Result: none -- -- Security: Execute permission of this procedure is granted to public. -- Internally, this procedure will call sp_MSreplcheck_publish -- to make sure that the caller is either server sysadmin or dbo -- of the publishing database. -- raiserror('Creating procedure sp_MSdropdynsnapshotvws', 0,1) go create procedure sp_MSdropdynsnapshotvws ( @dynamic_snapshot_views_table sysname ) as begin set nocount on declare @retcode int declare @final_retcode int declare @select_command nvarchar(4000) declare @old_dynamic_snapshot_view_name sysname declare @dynamic_snapshot_view_name sysname declare @drop_view_command nvarchar(4000) declare @select_param_list nvarchar(4000) select @final_retcode = 0 select @retcode = 0 -- Security check exec @retcode = sp_MSreplcheck_publish if @@error<>0 or @retcode<>0 begin return 1 end -- For the first fecth, don't do comparison with the old view name... select @dynamic_snapshot_view_name = null select @select_command = N' select @dynamic_snapshot_view_name = min(dynamic_snapshot_view_name) from ' + @dynamic_snapshot_views_table select @select_param_list = N'@dynamic_snapshot_view_name sysname output' exec @retcode = sp_executesql @select_command, @select_param_list, @dynamic_snapshot_view_name = @dynamic_snapshot_view_name output if @@error<>0 or @retcode<>0 begin select @final_retcode = 1 end -- Change query to do comparison with the old name select @select_command = N' select @dynamic_snapshot_view_name = min(dynamic_snapshot_view_name) from ' + @dynamic_snapshot_views_table + N' where dynamic_snapshot_view_name > @old_dynamic_snapshot_view_name' select @select_param_list = N'@dynamic_snapshot_view_name sysname output, @old_dynamic_snapshot_view_name sysname' -- Keep selecting view with the smallest name that -- is greater than the last one until we are done while @dynamic_snapshot_view_name is not null begin select @drop_view_command = N'drop view ' + quotename(@dynamic_snapshot_view_name) exec(@drop_view_command) if @@error<>0 begin select @final_retcode = 1 end delete MSdynamicsnapshotviews where dynamic_snapshot_view_name = @dynamic_snapshot_view_name if @@error<>0 begin select @final_retcode = 1 end select @old_dynamic_snapshot_view_name = @dynamic_snapshot_view_name select @dynamic_snapshot_view_name = null exec @retcode = sp_executesql @select_command, @select_param_list, @dynamic_snapshot_view_name = @dynamic_snapshot_view_name output, @old_dynamic_snapshot_view_name = @old_dynamic_snapshot_view_name if @@error<>0 or @retcode<>0 begin select @final_retcode = 1 end end -- At the end, we need to drop the temp table declare @drop_table_command nvarchar(4000) select @drop_table_command = N'drop table ' + @dynamic_snapshot_views_table exec(@drop_table_command) if @@error<>0 begin select @final_retcode = 1 end return @final_retcode end go exec sp_MS_marksystemobject 'sp_MSdropdynsnapshotvws' grant execute on sp_MSdropdynsnapshotvws to public go exec dbo.sp_configure 'allow updates',0 go dump tran master with no_log go reconfigure with override go